Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
75622c8
Merge pull request #2399 from PackRat-AI/main
mikib0 May 9, 2026
29fe5e0
fix(expo): update Google sign-in logic to use new authClient method
mikib0 May 9, 2026
2aee172
Merge pull request #2400 from PackRat-AI/fix/google-auth
mikib0 May 9, 2026
2e2377e
feat(web): wire gear-inventory and ai-chat screens to real API
andrew-bierman May 8, 2026
43a08a7
fix(ci): remove unsupported cache input from oven-sh/setup-bun@v2
andrew-bierman May 12, 2026
460c895
fix(web): address PR review comments on gear-inventory and ai screens
andrew-bierman May 12, 2026
2497119
fix(ci): add --frozen-lockfile to bun install in all CI workflows
andrew-bierman May 12, 2026
ed55dd1
fix(types): migrate trails to Better Auth client, align admin/user sc…
andrew-bierman May 12, 2026
5dc4210
chore(trails): sort package.json dependencies
andrew-bierman May 12, 2026
b0cfbee
fix(auth): remove LegacyAuth cast, migrate web app to Better Auth client
andrew-bierman May 12, 2026
34064aa
chore(web): sort package.json dependencies
andrew-bierman May 12, 2026
d409a03
refactor(web/auth): use useMutation for login/register instead of raw…
andrew-bierman May 12, 2026
f1f748b
fix(types): patch @packrat-ai/nativewindui to suppress ts-nocheck errors
andrew-bierman May 12, 2026
35cef0d
chore: sort package.json after bun patch
andrew-bierman May 12, 2026
29d2b70
fix(a11y): add aria-label to gear inventory search input
andrew-bierman May 12, 2026
23a0b28
Merge pull request #2398 from PackRat-AI/feat/web-wire-remaining-screens
andrew-bierman May 12, 2026
8b297c5
chore(biome): remove packages/units/src/index.ts from useMaxParams ex…
andrew-bierman May 12, 2026
e8c52a8
fix(units): refactor convert/displayWeight to satisfy useMaxParams rule
andrew-bierman May 12, 2026
c6e7094
Merge pull request #2387 from PackRat-AI/chore-biome-max-params-one
andrew-bierman May 13, 2026
1587e33
fix(auth): restore missing Apple logo on apple sign-in button
mikib0 May 14, 2026
b9e0862
fix(auth): remove setNativeProps call on OTP field focus
mikib0 May 14, 2026
a0e210f
feat(auth): replace Better Auth password reset with custom 6-digit OT…
mikib0 May 14, 2026
e1edb96
fix(auth): annotate password reset routes as public
mikib0 May 14, 2026
19b85f7
fix(auth): read session token from SecureStore to stop spurious reaut…
mikib0 May 14, 2026
3584f2c
Initial plan
Copilot Aug 20, 2025
277d6b5
Initial plan
Copilot Aug 20, 2025
d4d55cf
Initial plan
Copilot Aug 20, 2025
2ee7993
Initial plan
Copilot Aug 20, 2025
f9e0ced
Initial plan
Copilot Aug 22, 2025
65ee90b
Initial plan
Copilot Aug 22, 2025
268c484
Initial plan
Copilot Aug 23, 2025
9d8b9e0
Initial plan
Copilot Aug 23, 2025
4c603bf
Initial plan
Copilot Aug 20, 2025
cba730f
Initial plan
Copilot Aug 23, 2025
2098c0a
Initial plan
Copilot Aug 24, 2025
7c1d03f
Initial plan
Copilot Aug 23, 2025
74208a3
Initial plan
Copilot Aug 23, 2025
c6fe4c8
Initial plan
Copilot Sep 9, 2025
ea1a6f3
Initial plan
Copilot Sep 11, 2025
5a0b8b5
Initial plan
Copilot Sep 10, 2025
dbed3b3
Initial plan
Copilot Sep 11, 2025
fd2bca9
Initial plan
Copilot Sep 12, 2025
2a3927f
Initial plan
Copilot Oct 4, 2025
a1007a6
Initial plan
Copilot Oct 17, 2025
a62b9d9
Initial plan
Copilot Oct 17, 2025
07cedcc
Initial plan
Copilot Oct 18, 2025
5fb4623
Initial plan
Copilot Oct 20, 2025
f26491b
Initial plan
Copilot Oct 25, 2025
7e4c44e
Initial plan
Copilot Oct 26, 2025
c900bd7
Initial plan
Copilot Nov 18, 2025
12fd8fb
Initial plan
Copilot Nov 6, 2025
052cd01
Initial plan
Copilot Nov 6, 2025
6a605e3
Initial plan
Copilot Nov 8, 2025
0e7c2b7
Initial plan
Copilot Nov 8, 2025
e9d488e
Initial plan
Copilot Nov 9, 2025
66e23d7
Initial plan
Copilot Nov 9, 2025
691863f
Initial plan
Copilot Nov 11, 2025
ce38874
Initial plan
Copilot Nov 11, 2025
abf4e12
Initial plan
Copilot Nov 11, 2025
cc9ab97
Initial plan
Copilot Nov 11, 2025
076832c
Initial plan
Copilot Nov 18, 2025
ca6aaf3
Initial plan
Copilot Nov 18, 2025
ca56245
Initial plan
Copilot Nov 18, 2025
a00737a
Initial plan
Copilot Nov 18, 2025
4607681
Initial plan
Copilot Nov 18, 2025
c80a270
Initial plan
Copilot Nov 18, 2025
7b839c6
Initial plan
Copilot Nov 18, 2025
1a5c046
Initial plan
Copilot Nov 18, 2025
fb38aa3
Initial plan
Copilot Nov 18, 2025
c906445
Initial plan
Copilot Nov 18, 2025
3ebd3e5
Initial plan
Copilot Nov 18, 2025
871a6e6
Initial plan
Copilot Nov 18, 2025
6cd50be
Initial plan
Copilot Nov 18, 2025
c3ef508
Initial plan
Copilot Dec 2, 2025
5fa7fb2
Initial plan
Copilot Dec 13, 2025
eff86a3
Initial plan
Copilot Mar 9, 2026
490cc6e
Initial plan
Copilot Mar 9, 2026
9bee8b1
Initial plan
Copilot Mar 9, 2026
536c663
Initial plan
Copilot Mar 10, 2026
ac5db64
Initial plan
Copilot Mar 9, 2026
1316fb9
Initial plan
Copilot Mar 10, 2026
408ffa7
Initial plan
Copilot Feb 21, 2026
e731328
Initial plan
Copilot Feb 27, 2026
a3ff82d
Initial plan
Copilot Mar 10, 2026
ce6a013
Initial plan
Copilot Mar 7, 2026
97c410d
Initial plan
Copilot Mar 10, 2026
b868fdb
Initial plan
Copilot Mar 11, 2026
af7e4cf
Initial plan
Copilot Mar 9, 2026
fe59d53
Initial plan
Copilot Mar 9, 2026
23451c1
Initial plan
Copilot Mar 13, 2026
c49fb49
Initial plan
Copilot Mar 13, 2026
c661f5f
Initial plan
Copilot Mar 13, 2026
8f78c09
Initial plan
Copilot Mar 13, 2026
874253e
fix(expo/CreatePackItemForm): default quantity to 1 instead of 0
mikib0 Mar 13, 2026
bdb4e13
Initial plan
Copilot Mar 13, 2026
a43d4ba
Initial plan
Copilot Mar 16, 2026
c54c396
Initial plan
Copilot Mar 9, 2026
6091ec5
Initial plan
Copilot Mar 13, 2026
8994538
Initial plan
Copilot Mar 13, 2026
85e3d42
Initial plan
Copilot Mar 9, 2026
a66e360
Initial plan
Copilot Mar 9, 2026
9cb8e86
Initial plan
Copilot Apr 1, 2026
991b21b
Initial plan
Copilot Mar 21, 2026
1adcc0a
Initial plan
Copilot Mar 9, 2026
d3fad53
Initial plan
Copilot Mar 9, 2026
138325b
Initial plan
Copilot Mar 9, 2026
6c6cbb7
Initial plan
Copilot Mar 9, 2026
300f8d9
Initial plan
Copilot Mar 9, 2026
3182ec2
Initial plan
Copilot Mar 9, 2026
c1be87c
Initial plan
Copilot Mar 9, 2026
67b8a0d
Initial plan
Copilot Mar 9, 2026
e9f0a13
Initial plan
Copilot Mar 9, 2026
30aa570
Initial plan
Copilot Mar 9, 2026
17e1727
Initial plan
Copilot Aug 22, 2025
41cc410
Initial plan
Copilot Feb 27, 2026
dc6fb03
chore: reopen trigger (no-op commit to restore PR state)
andrew-bierman Apr 11, 2026
375cec3
ci: trigger biome check
andrew-bierman Apr 11, 2026
89af5bf
ci: retrigger CI after biome fixes
andrew-bierman Apr 11, 2026
84ac668
Initial plan
Copilot Mar 9, 2026
58e76d1
ci: trigger checks for dependabot merges
andrew-bierman Apr 13, 2026
9f0cb60
Initial plan
Copilot Apr 13, 2026
eeb762f
ci: retrigger checks after copilot merge-conflict fix
andrew-bierman Apr 14, 2026
06f0cc1
ci: trigger CI on Copilot bot's expo-symbols type fixes
andrew-bierman Apr 14, 2026
0fc5f35
Initial plan
Copilot Apr 14, 2026
d31aee0
trigger: retrigger CI after node_modules clean install verified vites…
andrew-bierman Apr 15, 2026
0c35b33
Initial plan
Copilot Apr 14, 2026
ea32f1f
trigger: retrigger CI after node_modules clean install verified vites…
andrew-bierman Apr 15, 2026
6fb579d
ci: re-trigger checks after @types/react alignment fix
claude Apr 16, 2026
fb2fea3
Initial plan
Copilot Apr 14, 2026
6ce180f
Initial plan
Copilot Apr 16, 2026
a9f51ad
ci: trigger CI run on updated branch
claude Apr 16, 2026
97937ba
Initial plan
Copilot Sep 22, 2025
b93b769
Initial plan
Copilot Sep 22, 2025
8c4fe51
ci: trigger workflows
andrew-bierman Apr 14, 2026
104cc3f
ci: retrigger CI after suspected transient runner failure
claude Apr 26, 2026
cc76f11
ci: retrigger workflows
andrew-bierman Apr 29, 2026
12bb727
fix(expo): null-safe color scheme toggle and geocode response check
andrew-bierman May 14, 2026
420f2eb
chore(expo/lib/api/packrat): improve typing
mikib0 May 14, 2026
cfcad31
chore(api/passwordResetService): resolve type issue
mikib0 May 14, 2026
38b8fd2
Merge pull request #2425 from PackRat-AI/fix/auth
mikib0 May 14, 2026
edbbe05
fix(etl): use raw SQL for completion write to bypass neon-http enum i…
andrew-bierman May 8, 2026
327512f
fix(etl): make catalog_items weight nullable + add ETL integration tests
andrew-bierman May 8, 2026
76a3ad9
fix(db): regenerate migration with drizzle-kit (0037_rich_electro)
andrew-bierman May 9, 2026
ae7c4b0
fix(etl): handle nullable weight/weightUnit when building pack items …
andrew-bierman May 12, 2026
0620808
fix(admin,etl): fix CORS Access-Control-Allow-Origin missing on prefl…
andrew-bierman May 12, 2026
5ef9b41
fix(types): resolve TypeScript errors blocking CI checks job
andrew-bierman May 12, 2026
d59d933
chore(deps): pin @packrat-ai/nativewindui to 2.0.3-2
andrew-bierman May 12, 2026
6c47e8c
feat(admin,etl): align TypeBox schemas with route columns + add ETL a…
andrew-bierman May 12, 2026
63eca7b
fix(etl): flush remaining items before updating totalProcessed + use …
andrew-bierman May 13, 2026
c7da8cd
fix(etl): yield to event loop every 100 rows instead of every row
andrew-bierman May 13, 2026
f475d03
fix(etl): respect stream backpressure to prevent Worker OOM on large …
andrew-bierman May 13, 2026
0fb11bf
fix(etl): raise cpu_ms limit to 400k (CF max) for large-file queue pr…
andrew-bierman May 13, 2026
9ecdad8
feat(etl): split large R2 files into 20 MB byte-range chunks at queue…
andrew-bierman May 13, 2026
e83583c
fix(admin,etl): address CodeRabbit/Copilot review comments
andrew-bierman May 12, 2026
5925ebf
fix(cors): add admin.packratai.com + *.workers.dev to root cors allow…
andrew-bierman May 12, 2026
7b915b4
fix(admin/analytics): coerce Neon int8 strings to Number() in overvie…
andrew-bierman May 12, 2026
ea8c729
fix(og): generate static PNG OG images for landing and guides static …
Copilot May 13, 2026
50445d7
chore(deps): enroll 47 third-party packages into Bun workspace catalog
andrew-bierman May 13, 2026
2fcba62
fix(guides): remove standalone bun.lock that broke Cloudflare Pages c…
andrew-bierman May 13, 2026
e36a3fa
fix(ci): add CF Pages-specific error message for missing GitHub Packa…
andrew-bierman May 14, 2026
9e340ad
fix(deps): replace vitest version pins with catalog: in landing and g…
andrew-bierman May 14, 2026
098adaf
fix(api): lower cpu_ms limit to 300000 — CF max is 300 s not 400 s
andrew-bierman May 14, 2026
78382fe
fix(etl): atomic totalProcessed updates and reset counters on retry
andrew-bierman May 14, 2026
721fad0
fixup: remove retry counter reset — incompatible with byte-range chun…
andrew-bierman May 14, 2026
ef675cc
chore: migrate trails/web deps to workspace catalog
andrew-bierman May 14, 2026
0be62e5
chore: regenerate bun.lock after catalog migration
andrew-bierman May 14, 2026
806f2f8
fix(etl): pass chunks instead of objectKeys to queueCatalogETL
andrew-bierman May 14, 2026
4fead9f
Merge pull request #2424 from PackRat-AI/chore/merge-main-into-develo…
andrew-bierman May 15, 2026
72c4e40
fix(auth): check __Secure- prefixed cookie when reading session token
mikib0 May 15, 2026
1917ea7
Merge pull request #2426 from PackRat-AI/fix/auth-secure-cookie-prefix
mikib0 May 15, 2026
54dfd07
chore: bump version to v2.0.26
mikib0 May 15, 2026
9c35486
chore: merge origin/main into release/2.0.26
mikib0 May 15, 2026
07d50ad
fix(trails): correct authClient import to use trailsAuthClient
mikib0 May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/api-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Run API tests
run: bun run --cwd packages/api test 2>&1 | tee /tmp/api-tests-output.log
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true
- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile
- name: Run Biome (check mode)
if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.fix == true) }}
run: bun biome check
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ jobs:
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ steps.bun-version.outputs.version }}
cache: true

# Sanity-check that the runner satisfies the repo's engine constraints.
- name: Verify runtime versions
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Setup Expo
uses: expo/expo-github-action@v8
Expand Down Expand Up @@ -343,7 +343,7 @@ jobs:
- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Setup Expo
uses: expo/expo-github-action@v8
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/lighthouse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
Expand Down Expand Up @@ -60,7 +59,6 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,11 @@ jobs:
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Determine target environment
id: env
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/release-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ jobs:
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
cache: true

- name: Validate release tag and app versions
run: bun .github/scripts/validate-release-version.ts "${{ steps.release_tag.outputs.tag }}"
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/sync-guides-r2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ jobs:
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Authenticate with GitHub for private packages
run: |
echo "PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV

- name: Install dependencies
run: bun install
run: bun install --frozen-lockfile
timeout-minutes: 5

- name: Sync guides to R2 bucket
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Run API unit tests
run: bun run --cwd packages/api test:unit:coverage
Expand All @@ -80,12 +79,11 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
cache: true

- name: Install dependencies
env:
PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN: ${{ secrets.PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN }}
run: bun install
run: bun install --frozen-lockfile

- name: Run Expo unit tests
run: bun run --cwd apps/expo test:coverage
Expand Down
2 changes: 1 addition & 1 deletion apps/admin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-admin-app",
"version": "2.0.25",
"version": "2.0.26",
"private": true,
"scripts": {
"build": "next build",
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default (): ExpoConfig =>
{
name: getAppName(),
slug: 'packrat',
version: '2.0.25',
version: '2.0.26',
scheme: 'packrat',
web: {
bundler: 'metro',
Expand Down
1 change: 1 addition & 0 deletions apps/expo/app/(app)/trip/location-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default function LocationSearchScreen() {
)}&key=${GOOGLE_MAPS_API_KEY}`,
);

if (!response.ok) throw new Error(`Geocode request failed: ${response.status}`);
const data = await response.json();

if (data.status === 'OK' && data.results.length > 0) {
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/app/auth/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default function AuthIndexScreen() {
size={Platform.select({ ios: 'lg', default: 'md' })}
onPress={signInWithApple}
>
<Text className="ios:text-foreground absolute left-4 text-[22px]"></Text>
<Text className="ios:text-foreground absolute left-4 text-[22px]">{''}</Text>
<Text className="ios:text-foreground">{t('auth.continueWithApple')}</Text>
</Button>
)}
Expand Down
10 changes: 1 addition & 9 deletions apps/expo/app/auth/one-time-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
type NativeSyntheticEvent,
Platform,
Pressable,
type TargetedEvent,
type TextInput,
type TextInputKeyPressEventData,
View,
Expand All @@ -27,7 +26,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');

const COUNTDOWN_SECONDS_TO_RESEND_CODE = 60;
const NUM_OF_CODE_CHARACTERS = 5;
const NUM_OF_CODE_CHARACTERS = 6;
const SCREEN_OPTIONS = {
headerBackTitle: 'Back',
headerTransparent: true,
Expand Down Expand Up @@ -266,12 +265,6 @@ function OTPField({
}
}

function onFocus(_e: NativeSyntheticEvent<TargetedEvent>) {
inputRef.current?.setNativeProps({
selection: { start: 0, end: value?.toString().length },
});
}

function onChangeText(text: string) {
setCodeValues((prev) => {
const values = [...prev];
Expand Down Expand Up @@ -311,7 +304,6 @@ ios:border ios:border-border ios:rounded-lg "
clearButtonMode="never"
materialHideActionIcons
materialRingColor={hasError ? colors.destructive : undefined}
onFocus={onFocus}
onKeyPress={onKeyPress}
onChangeText={onChangeText}
onSubmitEditing={
Expand Down
27 changes: 18 additions & 9 deletions apps/expo/features/auth/hooks/useAuthActions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { clientEnvs } from '@packrat/env/expo-client';
import { asBoolean, asString } from '@packrat/guards';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {
Expand Down Expand Up @@ -188,19 +189,27 @@ export function useAuthActions() {
};

const forgotPassword = async (email: string) => {
const { error } = await authClient.requestPasswordReset({
email,
redirectTo: 'packrat://reset-password',
const res = await fetch(`${clientEnvs.EXPO_PUBLIC_API_URL}/api/password-reset/request`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
if (error) throw new Error(error.message ?? 'Forgot password failed');
if (!res.ok) {
const data = (await res.json()) as { error?: string };
throw new Error(data.error ?? 'Forgot password failed');
}
};

const resetPassword = async (_email: string, opts: { token: string; newPassword: string }) => {
const { error } = await authClient.resetPassword({
token: opts.token,
newPassword: opts.newPassword,
const resetPassword = async (email: string, opts: { token: string; newPassword: string }) => {
const res = await fetch(`${clientEnvs.EXPO_PUBLIC_API_URL}/api/password-reset/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, code: opts.token, newPassword: opts.newPassword }),
});
if (error) throw new Error(error.message ?? 'Reset password failed');
if (!res.ok) {
const data = (await res.json()) as { error?: string };
throw new Error(data.error ?? 'Reset password failed');
}
Comment thread
mikib0 marked this conversation as resolved.
};

const verifyEmail = async (_email: string, token: string) => {
Expand Down
36 changes: 32 additions & 4 deletions apps/expo/lib/api/packrat.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
import { createApiClient } from '@packrat/api-client';
import { clientEnvs } from '@packrat/env/expo-client';
import { fromZod } from '@packrat/guards';
import { store } from 'expo-app/atoms/store';
import { needsReauthAtom } from 'expo-app/features/auth/atoms/authAtoms';
import { authClient } from 'expo-app/lib/auth-client';
import * as SecureStore from 'expo-secure-store';
import { z } from 'zod';

// The expoClient plugin serialises all cookies into SecureStore under this key.
// Parsing it locally avoids a network round-trip on every API request.
const COOKIE_STORE_KEY = 'packrat_cookie';

const CookieStoreSchema = z.record(z.object({ value: z.string() }));

// expoClient stores cookies as JSON: { "better-auth.session_token": { value, expires } }
// HTTPS servers (remote dev/prod) prefix the cookie name with __Secure-; HTTP (local) does not.
function parseSessionToken(cookieJson: string | null): string | null {
if (!cookieJson) return null;
const cookies = fromZod(CookieStoreSchema)(JSON.parse(cookieJson));
if (!cookies) return null;
Comment thread
mikib0 marked this conversation as resolved.
return (
cookies['better-auth.session_token']?.value ??
cookies['__Secure-better-auth.session_token']?.value ??
null
);
}

export const apiClient = createApiClient({
baseUrl: clientEnvs.EXPO_PUBLIC_API_URL,
auth: {
// Read the token from SecureStore — no network call on every API request.
getAccessToken: async () => {
const { data } = await authClient.getSession();
return data?.session?.token ?? null;
const cookieStr = await SecureStore.getItemAsync(COOKIE_STORE_KEY);
return parseSessionToken(cookieStr);
},
// Better Auth manages session renewal internally — no separate refresh token flow.
// Better Auth has no separate refresh-token endpoint; the 7-day session
// token is the only credential. Returning null here is intentional.
getRefreshToken: () => null,
onAccessTokenRefreshed: () => {},
onNeedsReauth: () => {
onNeedsReauth: async () => {
// A 401 can be transient (e.g. the server briefly returned an error).
// Verify the session is actually gone before alarming the user.
const { data } = await authClient.getSession();
if (data?.session) return;
store.set(needsReauthAtom, true);
Comment thread
mikib0 marked this conversation as resolved.
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/lib/hooks/useColorScheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function useColorScheme() {
}

function toggleColorScheme() {
return setColorScheme(colorScheme === 'light' ? 'dark' : 'light');
return setColorScheme((colorScheme ?? 'light') === 'light' ? 'dark' : 'light');
}

return {
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-expo-app",
"version": "2.0.25",
"version": "2.0.26",
"private": true,
"main": "expo-router/entry",
"scripts": {
Expand Down
20 changes: 10 additions & 10 deletions apps/expo/utils/__tests__/weight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,34 @@ function makeItem(
// ---------------------------------------------------------------------------
describe('convertWeight', () => {
it('returns the same value when from === to', () => {
expect(convertWeight(100, 'g', 'g')).toBe(100);
expect(convertWeight(5, 'oz', 'oz')).toBe(5);
expect(convertWeight(2, 'kg', 'kg')).toBe(2);
expect(convertWeight(1, 'lb', 'lb')).toBe(1);
expect(convertWeight(100, { from: 'g', to: 'g' })).toBe(100);
expect(convertWeight(5, { from: 'oz', to: 'oz' })).toBe(5);
expect(convertWeight(2, { from: 'kg', to: 'kg' })).toBe(2);
expect(convertWeight(1, { from: 'lb', to: 'lb' })).toBe(1);
});

it('converts grams to ounces', () => {
expect(convertWeight(100, 'g', 'oz')).toBeCloseTo(3.53, 1);
expect(convertWeight(100, { from: 'g', to: 'oz' })).toBeCloseTo(3.53, 1);
});

it('converts ounces to grams', () => {
expect(convertWeight(1, 'oz', 'g')).toBeCloseTo(28.349523125, 8);
expect(convertWeight(1, { from: 'oz', to: 'g' })).toBeCloseTo(28.349523125, 8);
});

it('converts grams to kilograms', () => {
expect(convertWeight(1000, 'g', 'kg')).toBe(1);
expect(convertWeight(1000, { from: 'g', to: 'kg' })).toBe(1);
});

it('converts kilograms to grams', () => {
expect(convertWeight(1, 'kg', 'g')).toBe(1000);
expect(convertWeight(1, { from: 'kg', to: 'g' })).toBe(1000);
});

it('converts grams to pounds', () => {
expect(convertWeight(453.59, 'g', 'lb')).toBeCloseTo(1, 1);
expect(convertWeight(453.59, { from: 'g', to: 'lb' })).toBeCloseTo(1, 1);
});

it('converts pounds to grams', () => {
expect(convertWeight(1, 'lb', 'g')).toBeCloseTo(453.59237, 4);
expect(convertWeight(1, { from: 'lb', to: 'g' })).toBeCloseTo(453.59237, 4);
});
});

Expand Down
2 changes: 1 addition & 1 deletion apps/guides/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-guides-app",
"version": "2.0.25",
"version": "2.0.26",
"private": true,
"scripts": {
"build": "bun run generate-og-images && bun run build-content && next build",
Expand Down
2 changes: 1 addition & 1 deletion apps/landing/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "packrat-landing-app",
"version": "2.0.25",
"version": "2.0.26",
"private": true,
"scripts": {
"build": "bun run generate-og-images && next build",
Expand Down
Loading
Loading