Compare commits

..

2 Commits

Author SHA1 Message Date
Max Koon
058f2bb94f refactor: move into monorepo 2025-11-08 13:37:55 -05:00
Max Koon
63670ff3b0 feat: add ui 2025-10-27 06:49:21 -04:00
51 changed files with 1355 additions and 2322 deletions

View File

@@ -1,41 +0,0 @@
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>
);
}

View File

@@ -8,7 +8,7 @@
}, },
"dependencies": { "dependencies": {
"@hono/node-server": "^1.19.5", "@hono/node-server": "^1.19.5",
"@money/shared": "link:../shared", "@money/shared": "workspace:*",
"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",

6
apps/expo/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli
expo-env.d.ts
# @end expo-cli

View File

@@ -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 '@/shared/src/auth'; import { authDataSchema } from '@money/shared/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 '@/shared/src'; import { schema, type Schema, createMutators, type Mutators, BASE_URL } from '@money/shared';
import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native"; import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native";
export const unstable_settings = { export const unstable_settings = {

View File

@@ -4,6 +4,7 @@ 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();
@@ -68,9 +69,7 @@ export default function HomeScreen() {
return ( return (
<View> <View>
<Link prefetch href="/settings"> <Header />
<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}}>

130
apps/expo/app/settings.tsx Normal file
View File

@@ -0,0 +1,130 @@
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>
);
}

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 384 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,70 @@
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>
)
}

61
apps/expo/package.json Normal file
View File

@@ -0,0 +1,61 @@
{
"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
}

View File

@@ -1,71 +1,17 @@
{ {
"name": "money", "name": "money",
"main": "expo-router/entry", "private": true,
"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"
] ]
} }
} }

View File

@@ -9,10 +9,10 @@
"./db": "./src/db/index.ts" "./db": "./src/db/index.ts"
}, },
"dependencies": { "dependencies": {
"drizzle-zero": "^0.15.1" "drizzle-zero": "^0.14.3"
}, },
"scripts": { "scripts": {
"generate:zero": "drizzle-zero generate -s ./src/db/schema/public.ts -o ./src/zero-schema.gen.ts -f --disable-legacy-queries --disable-legacy-mutators", "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",
"db:migrate": "drizzle-kit push" "db:migrate": "drizzle-kit push"
} }
} }

View File

@@ -93,9 +93,3 @@ 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(),
});

View File

@@ -1,4 +1,3 @@
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(
@@ -46,4 +45,13 @@ 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(),
});

View File

@@ -30,5 +30,11 @@ 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');
}) })
}; };

View File

@@ -10,9 +10,11 @@
import type { Row } from "@rocicorp/zero"; import type { Row } from "@rocicorp/zero";
import { createBuilder } from "@rocicorp/zero"; import { createBuilder } from "@rocicorp/zero";
import type { CustomType } from "drizzle-zero"; import type { DrizzleToZeroSchema, ZeroCustomType } 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.
@@ -25,8 +27,8 @@ export const schema = {
id: { id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"id" "id"
>, >,
@@ -34,8 +36,8 @@ export const schema = {
user_id: { user_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"user_id" "user_id"
>, >,
@@ -44,8 +46,8 @@ export const schema = {
plaid_id: { plaid_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"plaid_id" "plaid_id"
>, >,
@@ -54,8 +56,8 @@ export const schema = {
avaliable: { avaliable: {
type: "number", type: "number",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"avaliable" "avaliable"
>, >,
@@ -63,8 +65,8 @@ export const schema = {
current: { current: {
type: "number", type: "number",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"current" "current"
>, >,
@@ -72,8 +74,8 @@ export const schema = {
name: { name: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"name" "name"
>, >,
@@ -81,8 +83,8 @@ export const schema = {
createdAt: { createdAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"createdAt" "createdAt"
>, >,
@@ -91,8 +93,8 @@ export const schema = {
updatedAt: { updatedAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"balance", "balance",
"updatedAt" "updatedAt"
>, >,
@@ -101,14 +103,77 @@ 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 CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"plaidLink", "plaidLink",
"id" "id"
>, >,
@@ -116,8 +181,8 @@ export const schema = {
user_id: { user_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"plaidLink", "plaidLink",
"user_id" "user_id"
>, >,
@@ -125,8 +190,8 @@ export const schema = {
link: { link: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"plaidLink", "plaidLink",
"link" "link"
>, >,
@@ -134,8 +199,8 @@ export const schema = {
token: { token: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"plaidLink", "plaidLink",
"token" "token"
>, >,
@@ -143,8 +208,8 @@ export const schema = {
createdAt: { createdAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"plaidLink", "plaidLink",
"createdAt" "createdAt"
>, >,
@@ -159,8 +224,8 @@ export const schema = {
id: { id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"id" "id"
>, >,
@@ -168,8 +233,8 @@ export const schema = {
user_id: { user_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"user_id" "user_id"
>, >,
@@ -177,8 +242,8 @@ export const schema = {
plaid_id: { plaid_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"plaid_id" "plaid_id"
>, >,
@@ -186,8 +251,8 @@ export const schema = {
account_id: { account_id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"account_id" "account_id"
>, >,
@@ -195,8 +260,8 @@ export const schema = {
name: { name: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"name" "name"
>, >,
@@ -204,8 +269,8 @@ export const schema = {
amount: { amount: {
type: "number", type: "number",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"amount" "amount"
>, >,
@@ -213,8 +278,8 @@ export const schema = {
datetime: { datetime: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"datetime" "datetime"
>, >,
@@ -222,8 +287,8 @@ export const schema = {
authorized_datetime: { authorized_datetime: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"authorized_datetime" "authorized_datetime"
>, >,
@@ -231,8 +296,8 @@ export const schema = {
json: { json: {
type: "string", type: "string",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"json" "json"
>, >,
@@ -240,8 +305,8 @@ export const schema = {
createdAt: { createdAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"createdAt" "createdAt"
>, >,
@@ -250,8 +315,8 @@ export const schema = {
updatedAt: { updatedAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"transaction", "transaction",
"updatedAt" "updatedAt"
>, >,
@@ -266,8 +331,8 @@ export const schema = {
id: { id: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"id" "id"
>, >,
@@ -275,8 +340,8 @@ export const schema = {
name: { name: {
type: "string", type: "string",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"name" "name"
>, >,
@@ -284,8 +349,8 @@ export const schema = {
email: { email: {
type: "string", type: "string",
optional: false, optional: false,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"email" "email"
>, >,
@@ -293,8 +358,8 @@ export const schema = {
emailVerified: { emailVerified: {
type: "boolean", type: "boolean",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"emailVerified" "emailVerified"
>, >,
@@ -303,8 +368,8 @@ export const schema = {
image: { image: {
type: "string", type: "string",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"image" "image"
>, >,
@@ -312,8 +377,8 @@ export const schema = {
createdAt: { createdAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"createdAt" "createdAt"
>, >,
@@ -322,8 +387,8 @@ export const schema = {
updatedAt: { updatedAt: {
type: "number", type: "number",
optional: true, optional: true,
customType: null as unknown as CustomType< customType: null as unknown as ZeroCustomType<
typeof drizzleSchema, ZeroSchema,
"users", "users",
"updatedAt" "updatedAt"
>, >,
@@ -349,6 +414,11 @@ 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.

3063
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
nodeLinker: hoisted nodeLinker: hoisted
packages: packages:
- 'api' - 'apps/*'
- 'shared' - 'packages/*'

View File

@@ -29,14 +29,13 @@ processes:
command: "pnpm tsx ./scripts/set-machine-name.ts" command: "pnpm tsx ./scripts/set-machine-name.ts"
expo: expo:
command: "pnpm start" command: "pnpm --filter=@money/expo start"
depends_on: depends_on:
tailscale_machine_name: tailscale_machine_name:
condition: process_completed_successfully condition: process_completed_successfully
api: api:
command: "pnpm run dev" command: "pnpm --filter=@money/api dev"
working_dir: ./api
migrate: migrate:
command: | command: |
@@ -49,7 +48,7 @@ processes:
db: db:
condition: process_healthy condition: process_healthy
zero: zero:
command: npx zero-cache-dev -p shared/src/schema.ts command: npx zero-cache-dev -p packages/shared/src/schema.ts
depends_on: depends_on:
migrate: migrate:
condition: process_completed_successfully condition: process_completed_successfully