From 44924a502beaca6db601d903ad66e21e5daf1da4 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Fri, 1 May 2026 17:53:05 -0600 Subject: [PATCH 1/9] chore: scaffold @packrat/domain package Adds packages/domain with package.json, tsconfig, and barrel index files for packs, catalog, trips, and pack-templates sub-packages. Registers @packrat/domain/* paths in the root tsconfig. --- packages/domain/package.json | 18 ++++++++++++++++++ packages/domain/src/catalog/index.ts | 10 ++++++++++ .../domain/src}/catalog/types.ts | 0 .../src/catalog/utils}/normalizeDescription.ts | 0 packages/domain/src/index.ts | 4 ++++ packages/domain/src/pack-templates/index.ts | 8 ++++++++ .../domain/src}/pack-templates/types.ts | 0 packages/domain/src/packs/index.ts | 15 +++++++++++++++ .../domain/src}/packs/input.ts | 0 .../domain/src}/packs/types.ts | 0 .../src}/packs/utils/computePackWeights.ts | 0 .../src}/packs/utils/convertFromGrams.ts | 0 .../domain/src}/packs/utils/convertToGrams.ts | 0 packages/domain/src/trips/index.ts | 1 + .../domain/src}/trips/types.ts | 0 packages/domain/tsconfig.json | 10 ++++++++++ tsconfig.json | 2 ++ 17 files changed, 68 insertions(+) create mode 100644 packages/domain/package.json create mode 100644 packages/domain/src/catalog/index.ts rename {apps/expo/features => packages/domain/src}/catalog/types.ts (100%) rename {apps/expo/features/catalog/lib => packages/domain/src/catalog/utils}/normalizeDescription.ts (100%) create mode 100644 packages/domain/src/index.ts create mode 100644 packages/domain/src/pack-templates/index.ts rename {apps/expo/features => packages/domain/src}/pack-templates/types.ts (100%) create mode 100644 packages/domain/src/packs/index.ts rename {apps/expo/features => packages/domain/src}/packs/input.ts (100%) rename {apps/expo/features => packages/domain/src}/packs/types.ts (100%) rename {apps/expo/features => packages/domain/src}/packs/utils/computePackWeights.ts (100%) rename {apps/expo/features => packages/domain/src}/packs/utils/convertFromGrams.ts (100%) rename {apps/expo/features => packages/domain/src}/packs/utils/convertToGrams.ts (100%) create mode 100644 packages/domain/src/trips/index.ts rename {apps/expo/features => packages/domain/src}/trips/types.ts (100%) create mode 100644 packages/domain/tsconfig.json diff --git a/packages/domain/package.json b/packages/domain/package.json new file mode 100644 index 0000000000..c0571ab944 --- /dev/null +++ b/packages/domain/package.json @@ -0,0 +1,18 @@ +{ + "name": "@packrat/domain", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts", + "./catalog": "./src/catalog/index.ts", + "./pack-templates": "./src/pack-templates/index.ts", + "./packs": "./src/packs/index.ts", + "./trips": "./src/trips/index.ts" + }, + "main": "./src/index.ts", + "types": "./src/index.ts", + "dependencies": { + "@packrat/api": "workspace:*" + } +} diff --git a/packages/domain/src/catalog/index.ts b/packages/domain/src/catalog/index.ts new file mode 100644 index 0000000000..6a02bd2ff8 --- /dev/null +++ b/packages/domain/src/catalog/index.ts @@ -0,0 +1,10 @@ +export type { + CatalogItem, + CatalogItemInput, + CatalogItemLink, + CatalogItemReview, + CatalogItemWithPackItemFields, + CatalogItemWithQuantity, + PaginatedCatalogItemsResponse, +} from './types'; +export { normalizeDescription } from './utils/normalizeDescription'; diff --git a/apps/expo/features/catalog/types.ts b/packages/domain/src/catalog/types.ts similarity index 100% rename from apps/expo/features/catalog/types.ts rename to packages/domain/src/catalog/types.ts diff --git a/apps/expo/features/catalog/lib/normalizeDescription.ts b/packages/domain/src/catalog/utils/normalizeDescription.ts similarity index 100% rename from apps/expo/features/catalog/lib/normalizeDescription.ts rename to packages/domain/src/catalog/utils/normalizeDescription.ts diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts new file mode 100644 index 0000000000..8145601b36 --- /dev/null +++ b/packages/domain/src/index.ts @@ -0,0 +1,4 @@ +export * from './catalog/index'; +export * from './pack-templates/index'; +export * from './packs/index'; +export * from './trips/index'; diff --git a/packages/domain/src/pack-templates/index.ts b/packages/domain/src/pack-templates/index.ts new file mode 100644 index 0000000000..e229e374c3 --- /dev/null +++ b/packages/domain/src/pack-templates/index.ts @@ -0,0 +1,8 @@ +export type { + PackTemplate, + PackTemplateInput, + PackTemplateInStore, + PackTemplateItem, + PackTemplateItemInput, + WeightUnit, +} from './types'; diff --git a/apps/expo/features/pack-templates/types.ts b/packages/domain/src/pack-templates/types.ts similarity index 100% rename from apps/expo/features/pack-templates/types.ts rename to packages/domain/src/pack-templates/types.ts diff --git a/packages/domain/src/packs/index.ts b/packages/domain/src/packs/index.ts new file mode 100644 index 0000000000..38c9c34e27 --- /dev/null +++ b/packages/domain/src/packs/index.ts @@ -0,0 +1,15 @@ +export type { + Pack, + PackCategory, + PackInput, + PackInStore, + PackItem, + PackItemCategory, + PackItemInput, + PackWeightHistoryEntry, + Weight, + WeightUnit, +} from './types'; +export { computePackWeights } from './utils/computePackWeights'; +export { convertFromGrams } from './utils/convertFromGrams'; +export { convertToGrams } from './utils/convertToGrams'; diff --git a/apps/expo/features/packs/input.ts b/packages/domain/src/packs/input.ts similarity index 100% rename from apps/expo/features/packs/input.ts rename to packages/domain/src/packs/input.ts diff --git a/apps/expo/features/packs/types.ts b/packages/domain/src/packs/types.ts similarity index 100% rename from apps/expo/features/packs/types.ts rename to packages/domain/src/packs/types.ts diff --git a/apps/expo/features/packs/utils/computePackWeights.ts b/packages/domain/src/packs/utils/computePackWeights.ts similarity index 100% rename from apps/expo/features/packs/utils/computePackWeights.ts rename to packages/domain/src/packs/utils/computePackWeights.ts diff --git a/apps/expo/features/packs/utils/convertFromGrams.ts b/packages/domain/src/packs/utils/convertFromGrams.ts similarity index 100% rename from apps/expo/features/packs/utils/convertFromGrams.ts rename to packages/domain/src/packs/utils/convertFromGrams.ts diff --git a/apps/expo/features/packs/utils/convertToGrams.ts b/packages/domain/src/packs/utils/convertToGrams.ts similarity index 100% rename from apps/expo/features/packs/utils/convertToGrams.ts rename to packages/domain/src/packs/utils/convertToGrams.ts diff --git a/packages/domain/src/trips/index.ts b/packages/domain/src/trips/index.ts new file mode 100644 index 0000000000..03473f3154 --- /dev/null +++ b/packages/domain/src/trips/index.ts @@ -0,0 +1 @@ +export type { Trip, TripInput, TripInStore, TripStatus } from './types'; diff --git a/apps/expo/features/trips/types.ts b/packages/domain/src/trips/types.ts similarity index 100% rename from apps/expo/features/trips/types.ts rename to packages/domain/src/trips/types.ts diff --git a/packages/domain/tsconfig.json b/packages/domain/tsconfig.json new file mode 100644 index 0000000000..8d7bbc94df --- /dev/null +++ b/packages/domain/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "verbatimModuleSyntax": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/tsconfig.json b/tsconfig.json index 90b5240c22..85af917f4b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,6 +36,8 @@ "@packrat/analytics/*": ["./packages/analytics/src/*"], "@packrat/cli": ["./packages/cli/src"], "@packrat/cli/*": ["./packages/cli/src/*"], + "@packrat/domain": ["./packages/domain/src/index.ts"], + "@packrat/domain/*": ["./packages/domain/src/*"], "@packrat/web-ui": ["./packages/web-ui/src"], "@packrat/web-ui/*": ["./packages/web-ui/src/*"], "nativewindui/*": ["./apps/expo/components/ui/*"] From 91fdab06509f1b78f652383c6ade0c8307eead80 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Fri, 1 May 2026 17:53:31 -0600 Subject: [PATCH 2/9] refactor: wire expo app to @packrat/domain via re-export stubs - Fix imports in moved domain files to use @packrat/api/types instead of expo-app/* aliases - Add thin re-export stubs in expo features so all existing imports are unaffected (zero churn to callers) - Add @packrat/domain workspace dependency to expo package.json --- .../features/catalog/lib/normalizeDescription.ts | 1 + apps/expo/features/catalog/types.ts | 9 +++++++++ apps/expo/features/pack-templates/types.ts | 8 ++++++++ apps/expo/features/packs/input.ts | 1 + apps/expo/features/packs/types.ts | 12 ++++++++++++ apps/expo/features/packs/utils/computePackWeights.ts | 1 + apps/expo/features/packs/utils/convertFromGrams.ts | 1 + apps/expo/features/packs/utils/convertToGrams.ts | 1 + apps/expo/features/trips/types.ts | 1 + apps/expo/package.json | 1 + packages/domain/src/catalog/types.ts | 2 +- packages/domain/src/pack-templates/types.ts | 4 +++- packages/domain/src/packs/input.ts | 2 +- packages/domain/src/packs/types.ts | 6 +++--- packages/domain/src/trips/types.ts | 2 +- 15 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 apps/expo/features/catalog/lib/normalizeDescription.ts create mode 100644 apps/expo/features/catalog/types.ts create mode 100644 apps/expo/features/pack-templates/types.ts create mode 100644 apps/expo/features/packs/input.ts create mode 100644 apps/expo/features/packs/types.ts create mode 100644 apps/expo/features/packs/utils/computePackWeights.ts create mode 100644 apps/expo/features/packs/utils/convertFromGrams.ts create mode 100644 apps/expo/features/packs/utils/convertToGrams.ts create mode 100644 apps/expo/features/trips/types.ts diff --git a/apps/expo/features/catalog/lib/normalizeDescription.ts b/apps/expo/features/catalog/lib/normalizeDescription.ts new file mode 100644 index 0000000000..cedcd351b2 --- /dev/null +++ b/apps/expo/features/catalog/lib/normalizeDescription.ts @@ -0,0 +1 @@ +export { normalizeDescription } from '@packrat/domain/catalog'; diff --git a/apps/expo/features/catalog/types.ts b/apps/expo/features/catalog/types.ts new file mode 100644 index 0000000000..d6fc34b9fd --- /dev/null +++ b/apps/expo/features/catalog/types.ts @@ -0,0 +1,9 @@ +export type { + CatalogItem, + CatalogItemInput, + CatalogItemLink, + CatalogItemReview, + CatalogItemWithPackItemFields, + CatalogItemWithQuantity, + PaginatedCatalogItemsResponse, +} from '@packrat/domain/catalog'; diff --git a/apps/expo/features/pack-templates/types.ts b/apps/expo/features/pack-templates/types.ts new file mode 100644 index 0000000000..700ae97678 --- /dev/null +++ b/apps/expo/features/pack-templates/types.ts @@ -0,0 +1,8 @@ +export type { + PackTemplate, + PackTemplateInput, + PackTemplateInStore, + PackTemplateItem, + PackTemplateItemInput, + WeightUnit, +} from '@packrat/domain/pack-templates'; diff --git a/apps/expo/features/packs/input.ts b/apps/expo/features/packs/input.ts new file mode 100644 index 0000000000..8d011a9f33 --- /dev/null +++ b/apps/expo/features/packs/input.ts @@ -0,0 +1 @@ +export type { PackItemInput } from '@packrat/domain/packs'; diff --git a/apps/expo/features/packs/types.ts b/apps/expo/features/packs/types.ts new file mode 100644 index 0000000000..d2dc308315 --- /dev/null +++ b/apps/expo/features/packs/types.ts @@ -0,0 +1,12 @@ +export type { + Pack, + PackCategory, + PackInput, + PackInStore, + PackItem, + PackItemCategory, + PackItemInput, + PackWeightHistoryEntry, + Weight, + WeightUnit, +} from '@packrat/domain/packs'; diff --git a/apps/expo/features/packs/utils/computePackWeights.ts b/apps/expo/features/packs/utils/computePackWeights.ts new file mode 100644 index 0000000000..b5a4ceba43 --- /dev/null +++ b/apps/expo/features/packs/utils/computePackWeights.ts @@ -0,0 +1 @@ +export { computePackWeights } from '@packrat/domain/packs'; diff --git a/apps/expo/features/packs/utils/convertFromGrams.ts b/apps/expo/features/packs/utils/convertFromGrams.ts new file mode 100644 index 0000000000..9b4596b520 --- /dev/null +++ b/apps/expo/features/packs/utils/convertFromGrams.ts @@ -0,0 +1 @@ +export { convertFromGrams } from '@packrat/domain/packs'; diff --git a/apps/expo/features/packs/utils/convertToGrams.ts b/apps/expo/features/packs/utils/convertToGrams.ts new file mode 100644 index 0000000000..47702a08b2 --- /dev/null +++ b/apps/expo/features/packs/utils/convertToGrams.ts @@ -0,0 +1 @@ +export { convertToGrams } from '@packrat/domain/packs'; diff --git a/apps/expo/features/trips/types.ts b/apps/expo/features/trips/types.ts new file mode 100644 index 0000000000..057a1059cb --- /dev/null +++ b/apps/expo/features/trips/types.ts @@ -0,0 +1 @@ +export type { Trip, TripInput, TripInStore, TripStatus } from '@packrat/domain/trips'; diff --git a/apps/expo/package.json b/apps/expo/package.json index cbf86a55d9..a607967343 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -52,6 +52,7 @@ "@legendapp/state": "^3.0.0-beta.30", "@packrat/api-client": "workspace:*", "@packrat/config": "workspace:*", + "@packrat/domain": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@react-native-ai/apple": "~0.10.0", diff --git a/packages/domain/src/catalog/types.ts b/packages/domain/src/catalog/types.ts index 6c86cb7679..581c111235 100644 --- a/packages/domain/src/catalog/types.ts +++ b/packages/domain/src/catalog/types.ts @@ -1,4 +1,4 @@ -import type { WeightUnit } from 'expo-app/types'; +import type { WeightUnit } from '@packrat/api/types'; import type { PackItemInput } from '../packs/input'; export interface CatalogItemLink { diff --git a/packages/domain/src/pack-templates/types.ts b/packages/domain/src/pack-templates/types.ts index 6e7f7bf707..45934ded65 100644 --- a/packages/domain/src/pack-templates/types.ts +++ b/packages/domain/src/pack-templates/types.ts @@ -1,4 +1,6 @@ -export type WeightUnit = 'g' | 'kg' | 'oz' | 'lb'; +import type { WeightUnit } from '@packrat/api/types'; + +export type { WeightUnit }; export interface PackWeightHistoryEntry { id: string; diff --git a/packages/domain/src/packs/input.ts b/packages/domain/src/packs/input.ts index 8d9a4895e0..ac6329784f 100644 --- a/packages/domain/src/packs/input.ts +++ b/packages/domain/src/packs/input.ts @@ -1,4 +1,4 @@ -import type { WeightUnit } from 'expo-app/types'; +import type { WeightUnit } from '@packrat/api/types'; export interface PackItemInput { name: string; diff --git a/packages/domain/src/packs/types.ts b/packages/domain/src/packs/types.ts index 9b6f433895..93e659317a 100644 --- a/packages/domain/src/packs/types.ts +++ b/packages/domain/src/packs/types.ts @@ -1,6 +1,6 @@ -import type { CatalogItem } from 'expo-app/features/catalog/types'; -import type { PackTemplateItem } from 'expo-app/features/pack-templates/types'; -import type { PackCategory, WeightUnit } from 'expo-app/types'; +import type { PackCategory, WeightUnit } from '@packrat/api/types'; +import type { CatalogItem } from '../catalog/types'; +import type { PackTemplateItem } from '../pack-templates/types'; export type { PackCategory, WeightUnit }; diff --git a/packages/domain/src/trips/types.ts b/packages/domain/src/trips/types.ts index 2d59bb5b50..31256b37df 100644 --- a/packages/domain/src/trips/types.ts +++ b/packages/domain/src/trips/types.ts @@ -1,4 +1,4 @@ -import type { Pack } from 'expo-app/features/packs/types'; +import type { Pack } from '../packs/types'; export type TripStatus = 'planned' | 'ongoing' | 'completed' | 'cancelled'; From e36e19287cb02f06c8117202421acf3c6ab2bbcf Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Fri, 1 May 2026 18:02:47 -0600 Subject: [PATCH 3/9] =?UTF-8?q?refactor:=20rename=20@packrat/domain=20?= =?UTF-8?q?=E2=86=92=20@packrat/app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renames packages/domain → packages/app and updates all references. The shared package will hold full FSD feature slices (types, hooks, utils, and .web.tsx UI variants) — not just domain logic — so 'app' is the more accurate name. Expo and web apps become thin routing shells on top of packages/app. --- .../catalog/lib/normalizeDescription.ts | 2 +- apps/expo/features/catalog/types.ts | 2 +- apps/expo/features/pack-templates/types.ts | 2 +- apps/expo/features/packs/input.ts | 2 +- apps/expo/features/packs/types.ts | 2 +- .../packs/utils/computePackWeights.ts | 2 +- .../features/packs/utils/convertFromGrams.ts | 2 +- .../features/packs/utils/convertToGrams.ts | 2 +- apps/expo/features/trips/types.ts | 2 +- apps/expo/package.json | 2 +- compound-engineering.local.md | 10 +++ ...hared-app-package-extraction-brainstorm.md | 69 +++++++++++++++++++ packages/{domain => app}/package.json | 2 +- packages/{domain => app}/src/catalog/index.ts | 0 packages/{domain => app}/src/catalog/types.ts | 0 .../src/catalog/utils/normalizeDescription.ts | 0 packages/{domain => app}/src/index.ts | 0 .../src/pack-templates/index.ts | 0 .../src/pack-templates/types.ts | 0 packages/{domain => app}/src/packs/index.ts | 0 packages/{domain => app}/src/packs/input.ts | 0 packages/{domain => app}/src/packs/types.ts | 0 .../src/packs/utils/computePackWeights.ts | 0 .../src/packs/utils/convertFromGrams.ts | 0 .../src/packs/utils/convertToGrams.ts | 0 packages/{domain => app}/src/trips/index.ts | 0 packages/{domain => app}/src/trips/types.ts | 0 packages/{domain => app}/tsconfig.json | 0 todos/001-pending-p1-toggle-api-mismatch.md | 59 ++++++++++++++++ ...2-pending-p1-missing-react-type-imports.md | 45 ++++++++++++ ...nding-p1-securetext-multiline-plaintext.md | 48 +++++++++++++ ...04-pending-p2-context-menu-methods-type.md | 43 ++++++++++++ ...pending-p2-duplicate-error-message-prop.md | 35 ++++++++++ ...006-pending-p2-context-submenu-mutation.md | 45 ++++++++++++ .../007-pending-p2-web-ui-depdendency-type.md | 35 ++++++++++ ...008-pending-p2-submit-editing-not-wired.md | 44 ++++++++++++ ...09-pending-p2-unsafe-rest-spread-button.md | 43 ++++++++++++ ...ng-p2-icon-map-nouncheckedindexedaccess.md | 41 +++++++++++ todos/011-pending-p3-cn-utility-duplicated.md | 46 +++++++++++++ ...pending-p3-theme-toggle-circular-import.md | 37 ++++++++++ todos/013-pending-p3-alert-methods-type.md | 40 +++++++++++ ...pending-p3-create-item-generic-identity.md | 45 ++++++++++++ todos/015-pending-p3-lucide-barrel-imports.md | 44 ++++++++++++ .../016-pending-p3-picker-child-type-guard.md | 52 ++++++++++++++ tsconfig.json | 4 +- 45 files changed, 794 insertions(+), 13 deletions(-) create mode 100644 compound-engineering.local.md create mode 100644 docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md rename packages/{domain => app}/package.json (93%) rename packages/{domain => app}/src/catalog/index.ts (100%) rename packages/{domain => app}/src/catalog/types.ts (100%) rename packages/{domain => app}/src/catalog/utils/normalizeDescription.ts (100%) rename packages/{domain => app}/src/index.ts (100%) rename packages/{domain => app}/src/pack-templates/index.ts (100%) rename packages/{domain => app}/src/pack-templates/types.ts (100%) rename packages/{domain => app}/src/packs/index.ts (100%) rename packages/{domain => app}/src/packs/input.ts (100%) rename packages/{domain => app}/src/packs/types.ts (100%) rename packages/{domain => app}/src/packs/utils/computePackWeights.ts (100%) rename packages/{domain => app}/src/packs/utils/convertFromGrams.ts (100%) rename packages/{domain => app}/src/packs/utils/convertToGrams.ts (100%) rename packages/{domain => app}/src/trips/index.ts (100%) rename packages/{domain => app}/src/trips/types.ts (100%) rename packages/{domain => app}/tsconfig.json (100%) create mode 100644 todos/001-pending-p1-toggle-api-mismatch.md create mode 100644 todos/002-pending-p1-missing-react-type-imports.md create mode 100644 todos/003-pending-p1-securetext-multiline-plaintext.md create mode 100644 todos/004-pending-p2-context-menu-methods-type.md create mode 100644 todos/005-pending-p2-duplicate-error-message-prop.md create mode 100644 todos/006-pending-p2-context-submenu-mutation.md create mode 100644 todos/007-pending-p2-web-ui-depdendency-type.md create mode 100644 todos/008-pending-p2-submit-editing-not-wired.md create mode 100644 todos/009-pending-p2-unsafe-rest-spread-button.md create mode 100644 todos/010-pending-p2-icon-map-nouncheckedindexedaccess.md create mode 100644 todos/011-pending-p3-cn-utility-duplicated.md create mode 100644 todos/012-pending-p3-theme-toggle-circular-import.md create mode 100644 todos/013-pending-p3-alert-methods-type.md create mode 100644 todos/014-pending-p3-create-item-generic-identity.md create mode 100644 todos/015-pending-p3-lucide-barrel-imports.md create mode 100644 todos/016-pending-p3-picker-child-type-guard.md diff --git a/apps/expo/features/catalog/lib/normalizeDescription.ts b/apps/expo/features/catalog/lib/normalizeDescription.ts index cedcd351b2..38944cfb94 100644 --- a/apps/expo/features/catalog/lib/normalizeDescription.ts +++ b/apps/expo/features/catalog/lib/normalizeDescription.ts @@ -1 +1 @@ -export { normalizeDescription } from '@packrat/domain/catalog'; +export { normalizeDescription } from '@packrat/app/catalog'; diff --git a/apps/expo/features/catalog/types.ts b/apps/expo/features/catalog/types.ts index d6fc34b9fd..53430d9132 100644 --- a/apps/expo/features/catalog/types.ts +++ b/apps/expo/features/catalog/types.ts @@ -6,4 +6,4 @@ export type { CatalogItemWithPackItemFields, CatalogItemWithQuantity, PaginatedCatalogItemsResponse, -} from '@packrat/domain/catalog'; +} from '@packrat/app/catalog'; diff --git a/apps/expo/features/pack-templates/types.ts b/apps/expo/features/pack-templates/types.ts index 700ae97678..654d23baa5 100644 --- a/apps/expo/features/pack-templates/types.ts +++ b/apps/expo/features/pack-templates/types.ts @@ -5,4 +5,4 @@ export type { PackTemplateItem, PackTemplateItemInput, WeightUnit, -} from '@packrat/domain/pack-templates'; +} from '@packrat/app/pack-templates'; diff --git a/apps/expo/features/packs/input.ts b/apps/expo/features/packs/input.ts index 8d011a9f33..0b58053a5c 100644 --- a/apps/expo/features/packs/input.ts +++ b/apps/expo/features/packs/input.ts @@ -1 +1 @@ -export type { PackItemInput } from '@packrat/domain/packs'; +export type { PackItemInput } from '@packrat/app/packs'; diff --git a/apps/expo/features/packs/types.ts b/apps/expo/features/packs/types.ts index d2dc308315..a60a4c3efd 100644 --- a/apps/expo/features/packs/types.ts +++ b/apps/expo/features/packs/types.ts @@ -9,4 +9,4 @@ export type { PackWeightHistoryEntry, Weight, WeightUnit, -} from '@packrat/domain/packs'; +} from '@packrat/app/packs'; diff --git a/apps/expo/features/packs/utils/computePackWeights.ts b/apps/expo/features/packs/utils/computePackWeights.ts index b5a4ceba43..75c1a4da35 100644 --- a/apps/expo/features/packs/utils/computePackWeights.ts +++ b/apps/expo/features/packs/utils/computePackWeights.ts @@ -1 +1 @@ -export { computePackWeights } from '@packrat/domain/packs'; +export { computePackWeights } from '@packrat/app/packs'; diff --git a/apps/expo/features/packs/utils/convertFromGrams.ts b/apps/expo/features/packs/utils/convertFromGrams.ts index 9b4596b520..93b9b48eed 100644 --- a/apps/expo/features/packs/utils/convertFromGrams.ts +++ b/apps/expo/features/packs/utils/convertFromGrams.ts @@ -1 +1 @@ -export { convertFromGrams } from '@packrat/domain/packs'; +export { convertFromGrams } from '@packrat/app/packs'; diff --git a/apps/expo/features/packs/utils/convertToGrams.ts b/apps/expo/features/packs/utils/convertToGrams.ts index 47702a08b2..2eb5f4722b 100644 --- a/apps/expo/features/packs/utils/convertToGrams.ts +++ b/apps/expo/features/packs/utils/convertToGrams.ts @@ -1 +1 @@ -export { convertToGrams } from '@packrat/domain/packs'; +export { convertToGrams } from '@packrat/app/packs'; diff --git a/apps/expo/features/trips/types.ts b/apps/expo/features/trips/types.ts index 057a1059cb..973263f643 100644 --- a/apps/expo/features/trips/types.ts +++ b/apps/expo/features/trips/types.ts @@ -1 +1 @@ -export type { Trip, TripInput, TripInStore, TripStatus } from '@packrat/domain/trips'; +export type { Trip, TripInput, TripInStore, TripStatus } from '@packrat/app/trips'; diff --git a/apps/expo/package.json b/apps/expo/package.json index a607967343..cd67a162c6 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -51,8 +51,8 @@ "@gorhom/bottom-sheet": "^5.1.2", "@legendapp/state": "^3.0.0-beta.30", "@packrat/api-client": "workspace:*", + "@packrat/app": "workspace:*", "@packrat/config": "workspace:*", - "@packrat/domain": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@react-native-ai/apple": "~0.10.0", diff --git a/compound-engineering.local.md b/compound-engineering.local.md new file mode 100644 index 0000000000..a9a62e0b25 --- /dev/null +++ b/compound-engineering.local.md @@ -0,0 +1,10 @@ +--- +review_agents: + - kieran-typescript-reviewer + - architecture-strategist + - performance-oracle + - security-sentinel + - code-simplicity-reviewer +--- + +This is a React Native / Expo monorepo (Bun workspaces) with a web layer powered by Metro `.web.tsx` platform shims that map `@packrat-ai/nativewindui` to `@packrat/web-ui` (shadcn/Radix UI components). TypeScript strict mode. Biome for linting/formatting. The primary concern is correctness of the shim layer and ensuring no regressions to the native mobile builds. diff --git a/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md b/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md new file mode 100644 index 0000000000..8987a04142 --- /dev/null +++ b/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md @@ -0,0 +1,69 @@ +--- +date: 2026-05-01 +topic: shared-app-package-extraction +--- + +# Shared Logic Extraction — How Much Can Move to a `packages/domain` Package? + +## What We're Building + +A shared `packages/domain` package (or `packages/app`) containing platform-agnostic business logic: types, domain utils, data-fetching hooks, and Jotai store definitions — usable by both the Expo app today and a future Next.js web app without changes to either consumer. + +## Why This Approach + +The monorepo already proves the pattern works: `@packrat/api-client` is fully platform-agnostic because it injects auth adapters from the consumer layer rather than importing them directly. The same injection pattern can unlock 35–40% of feature code in `apps/expo/features/` for sharing. Screens and components (the irreducibly platform-specific 40%) stay in each app. Logic moves to shared packages. + +## Current State (from audit) + +**Already shared:** `api-client`, `guards`, `env`, `config` — well-architected boundaries. + +**`apps/expo/features/` breakdown (307 files):** + +| Category | Files | Shareable? | +|---|---|---| +| Screens (`screens/*.tsx`) | 39 | No — expo-router, RN layout primitives | +| Components (`components/*.tsx`) | 84 | No — View, Text, TouchableOpacity | +| Hooks (`hooks/*.ts`) | 98 | ~30% clean now, ~70% light surgery | +| Stores/Atoms | 18 | Medium — logic clean, persistence RN | +| Utils + Types | ~68 | ~60% clean, ~40% RN/Expo | + +**Rough extractable: 35–40% of feature code.** + +## Key Decisions + +**1. The injection pattern is the unlock.** +`@packrat/api-client` already does this for auth. Apply the same to: +- **Navigation**: hooks pass callbacks (`onSuccess: () => router.push(...)`) rather than importing `expo-router` directly +- **Storage**: Jotai atom persistence layer injected at app startup, not inside the atom definition +- **Image picking**: `useImagePicker` wraps `expo-image-picker`; extract the mutation logic, inject the picker as a callback + +**2. Easy wins (zero refactor, move tomorrow):** +- `packs/utils/convertToGrams.ts`, `convertFromGrams.ts`, `computePackWeights.ts` — unit math with tests +- `catalog/lib/normalizeDescription.ts` — string formatter +- All domain types: `packs/types.ts`, `trips/types.ts`, `catalog/types.ts` +- ~29 hooks that grep clean (no `react-native`/`expo` imports): `useAllPacks`, `useRecentPacks`, `useUserPackItems`, `useCategoriesCount`, etc. + +**3. Medium effort (~30 min per hook/store):** +- `useCreatePack`, `useUpdatePack` etc.: query logic is clean; navigation side-effects are the only entanglement. Lift navigation out as an `onSuccess` callback parameter. +- Jotai stores: atom *definitions* are platform-agnostic; only `persistPlugin` + `kvStorage` are Expo-specific. Split: `domain` exports the atom shape, Expo app wraps with persistence. +- `authAtoms.ts`: imports `kvStorage` (MMKV). Same pattern — inject the storage adapter. + +**4. Screens and components do NOT move.** They are irreducibly platform-specific by definition. This is fine — the value is in sharing the logic layer, not the view layer. + +**5. JSX/logic split assessment:** +The split already mostly exists structurally (`.ts` hooks vs `.tsx` screens). The entanglement is in **platform side-effects used inside otherwise pure hooks** — specifically `expo-router` for navigation callbacks and `expo-*` for device APIs. These can be lifted out via the injection pattern without restructuring anything major. + +## What This Enables + +A future Next.js app can `import { useAllPacks, computePackWeights } from '@packrat/domain'` and get all the data-fetching and business logic for free. It builds its own screens and components on top. No shared JSX, no shared navigation — just shared models and queries. + +## Open Questions + +- **Package name**: `packages/domain`, `packages/app`, or `packages/core`? Domain is most accurate. +- **Migration strategy**: move all at once vs feature-by-feature? Feature-by-feature (start with `packs`) reduces risk. +- **Jotai on Next.js**: Jotai works fine in Next.js App Router with a Provider. Not a blocker. +- **tRPC/eden client on Next.js**: `@packrat/api-client` already platform-agnostic. Needs HTTP transport config, not a code change. + +## Next Steps + +→ `/ce:plan` to create `packages/domain` with the packs feature as the first migration target. diff --git a/packages/domain/package.json b/packages/app/package.json similarity index 93% rename from packages/domain/package.json rename to packages/app/package.json index c0571ab944..94950fd70b 100644 --- a/packages/domain/package.json +++ b/packages/app/package.json @@ -1,5 +1,5 @@ { - "name": "@packrat/domain", + "name": "@packrat/app", "version": "0.1.0", "private": true, "type": "module", diff --git a/packages/domain/src/catalog/index.ts b/packages/app/src/catalog/index.ts similarity index 100% rename from packages/domain/src/catalog/index.ts rename to packages/app/src/catalog/index.ts diff --git a/packages/domain/src/catalog/types.ts b/packages/app/src/catalog/types.ts similarity index 100% rename from packages/domain/src/catalog/types.ts rename to packages/app/src/catalog/types.ts diff --git a/packages/domain/src/catalog/utils/normalizeDescription.ts b/packages/app/src/catalog/utils/normalizeDescription.ts similarity index 100% rename from packages/domain/src/catalog/utils/normalizeDescription.ts rename to packages/app/src/catalog/utils/normalizeDescription.ts diff --git a/packages/domain/src/index.ts b/packages/app/src/index.ts similarity index 100% rename from packages/domain/src/index.ts rename to packages/app/src/index.ts diff --git a/packages/domain/src/pack-templates/index.ts b/packages/app/src/pack-templates/index.ts similarity index 100% rename from packages/domain/src/pack-templates/index.ts rename to packages/app/src/pack-templates/index.ts diff --git a/packages/domain/src/pack-templates/types.ts b/packages/app/src/pack-templates/types.ts similarity index 100% rename from packages/domain/src/pack-templates/types.ts rename to packages/app/src/pack-templates/types.ts diff --git a/packages/domain/src/packs/index.ts b/packages/app/src/packs/index.ts similarity index 100% rename from packages/domain/src/packs/index.ts rename to packages/app/src/packs/index.ts diff --git a/packages/domain/src/packs/input.ts b/packages/app/src/packs/input.ts similarity index 100% rename from packages/domain/src/packs/input.ts rename to packages/app/src/packs/input.ts diff --git a/packages/domain/src/packs/types.ts b/packages/app/src/packs/types.ts similarity index 100% rename from packages/domain/src/packs/types.ts rename to packages/app/src/packs/types.ts diff --git a/packages/domain/src/packs/utils/computePackWeights.ts b/packages/app/src/packs/utils/computePackWeights.ts similarity index 100% rename from packages/domain/src/packs/utils/computePackWeights.ts rename to packages/app/src/packs/utils/computePackWeights.ts diff --git a/packages/domain/src/packs/utils/convertFromGrams.ts b/packages/app/src/packs/utils/convertFromGrams.ts similarity index 100% rename from packages/domain/src/packs/utils/convertFromGrams.ts rename to packages/app/src/packs/utils/convertFromGrams.ts diff --git a/packages/domain/src/packs/utils/convertToGrams.ts b/packages/app/src/packs/utils/convertToGrams.ts similarity index 100% rename from packages/domain/src/packs/utils/convertToGrams.ts rename to packages/app/src/packs/utils/convertToGrams.ts diff --git a/packages/domain/src/trips/index.ts b/packages/app/src/trips/index.ts similarity index 100% rename from packages/domain/src/trips/index.ts rename to packages/app/src/trips/index.ts diff --git a/packages/domain/src/trips/types.ts b/packages/app/src/trips/types.ts similarity index 100% rename from packages/domain/src/trips/types.ts rename to packages/app/src/trips/types.ts diff --git a/packages/domain/tsconfig.json b/packages/app/tsconfig.json similarity index 100% rename from packages/domain/tsconfig.json rename to packages/app/tsconfig.json diff --git a/todos/001-pending-p1-toggle-api-mismatch.md b/todos/001-pending-p1-toggle-api-mismatch.md new file mode 100644 index 0000000000..7f748bfefd --- /dev/null +++ b/todos/001-pending-p1-toggle-api-mismatch.md @@ -0,0 +1,59 @@ +--- +status: pending +priority: p1 +issue_id: "001" +tags: [code-review, architecture, runtime-bug, web-shims] +dependencies: [] +--- + +# Toggle API Mismatch — Silent Runtime Bug + +## Problem Statement + +`index.web.tsx` exports `Switch as Toggle` directly from `@packrat/web-ui`. The shadcn `Switch` uses `checked` + `onCheckedChange`, but nativewindui's `Toggle` (wrapping RN's `Switch`) uses `value` + `onValueChange`. Screen code passes `value`/`onValueChange` (e.g. `weather-alert-preferences.tsx` lines 96–116). On web, the Radix Switch receives unknown props — the toggle renders visually but never responds to interaction. + +**Why it matters:** Toggle UI appears functional but is completely broken. Users cannot change toggle state on web. No error is thrown. + +## Findings + +- Architecture reviewer confirmed screen code at `apps/expo/app/(app)/weather-alert-preferences.tsx:96–116` passes `value`/`onValueChange` +- `@packrat/web-ui` `Switch` component uses `checked`/`onCheckedChange` (Radix UI) +- Direct re-export `Switch as Toggle` provides no prop bridging + +## Proposed Solutions + +### Option A: Wrapper component in index.web.tsx (Recommended) +```tsx +export function Toggle({ value, onValueChange, checked, onCheckedChange, disabled, ...rest }) { + return ( + + ); +} +``` +**Pros:** Handles both native and shadcn props, drop-in fix. +**Cons:** Must keep in sync if Toggle native API evolves. +**Effort:** Small. **Risk:** Low. + +### Option B: Dedicated `toggle.web.tsx` shim file +Same logic but in its own file, imported by the barrel. +**Pros:** Consistent with other shim files. +**Cons:** One more file. +**Effort:** Small. **Risk:** Low. + +## Acceptance Criteria +- [ ] Toggle renders correctly on web +- [ ] Toggling state updates correctly (onValueChange fires) +- [ ] `bun check-types` still passes +- [ ] Biome passes + +## Work Log +- 2026-05-01: Found during ce:review (architecture-strategist agent) + +## Resources +- `packages/ui/nativewindui/index.web.tsx` — current direct re-export +- `apps/expo/app/(app)/weather-alert-preferences.tsx:96–116` — confirmed call site diff --git a/todos/002-pending-p1-missing-react-type-imports.md b/todos/002-pending-p1-missing-react-type-imports.md new file mode 100644 index 0000000000..dc730fe305 --- /dev/null +++ b/todos/002-pending-p1-missing-react-type-imports.md @@ -0,0 +1,45 @@ +--- +status: pending +priority: p1 +issue_id: "002" +tags: [code-review, typescript, web-shims] +dependencies: [] +--- + +# Missing `import type * as React` in Three Shim Files + +## Problem Statement + +`text-field.web.tsx`, `search-input.web.tsx`, and `icon.web.tsx` use `React.` namespace for type references (`React.ChangeEventHandler`, `React.ReactNode`, `React.CSSProperties`, `React.Ref`, `React.ChangeEvent`) but none import the `React` namespace. The new JSX transform handles JSX without a React import, but it does not inject the namespace for explicit type references. TypeScript will error on these under `strict: true`. + +## Findings + +From TypeScript reviewer: +- `text-field.web.tsx`: uses `React.ChangeEventHandler`, `React.ReactNode`, `React.CSSProperties`, `React.Ref` +- `search-input.web.tsx`: uses `React.ChangeEventHandler`, `React.ReactNode` +- `icon.web.tsx`: uses `React.CSSProperties` +- All three import named exports from `react` (forwardRef, useId, useEffect) but not the namespace + +## Proposed Solutions + +### Option A: Add `import type * as React from 'react'` (Recommended) +Add to each of the three files. The `type` modifier keeps it type-only with zero bundle impact. +**Effort:** Small. **Risk:** None. + +### Option B: Replace namespace usage with direct type imports +e.g., `import type { CSSProperties } from 'react'` and replace `React.CSSProperties` → `CSSProperties` +**Pros:** More explicit, no namespace needed. +**Cons:** More lines to change. +**Effort:** Small. **Risk:** None. + +## Acceptance Criteria +- [ ] `bun check-types` passes with no React namespace errors +- [ ] Biome passes (import should be `import type`) + +## Work Log +- 2026-05-01: Found during ce:review (kieran-typescript-reviewer) + +## Resources +- `packages/ui/nativewindui/text-field.web.tsx` +- `packages/ui/nativewindui/search-input.web.tsx` +- `packages/ui/nativewindui/icon.web.tsx` diff --git a/todos/003-pending-p1-securetext-multiline-plaintext.md b/todos/003-pending-p1-securetext-multiline-plaintext.md new file mode 100644 index 0000000000..244aca5c42 --- /dev/null +++ b/todos/003-pending-p1-securetext-multiline-plaintext.md @@ -0,0 +1,48 @@ +--- +status: pending +priority: p1 +issue_id: "003" +tags: [code-review, security, web-shims] +dependencies: [] +--- + +# secureTextEntry + multiline Silently Renders Passwords as Plaintext + +## Problem Statement + +In `text-field.web.tsx`, when `multiline={true}` a `