From 016e849d3ed2be8edf8ecfa11dab7593bf6e6722 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 18 Aug 2025 19:26:59 -0600 Subject: [PATCH 01/12] fix: Fix onboard-meeting-members deep link with groupId. --- package.json | 1 + src/interfaces/deepLinks.ts | 28 +++++++++++++++++-------- src/services/deepLinks.ts | 28 +++++++++++++++++-------- src/views/DeepLinkErrorView.vue | 4 ++-- src/views/OnboardMeetingMembersView.vue | 2 +- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index eb68f859..bdb48dd0 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "type-check": "tsc --noEmit", "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js", "test:prerequisites": "node scripts/check-prerequisites.js", + "test:all": "npm run lint && tsc && npm run test:web && npm run test:mobile && ./scripts/test-safety-check.sh && echo '\n\n\nGotta add the performance tests'", "test:web": "npx playwright test -c playwright.config-local.ts --trace on", "test:mobile": "./scripts/test-mobile.sh", "test:android": "node scripts/test-android.js", diff --git a/src/interfaces/deepLinks.ts b/src/interfaces/deepLinks.ts index d5266c7a..0fe5c68d 100644 --- a/src/interfaces/deepLinks.ts +++ b/src/interfaces/deepLinks.ts @@ -28,7 +28,7 @@ import { z } from "zod"; // Parameter validation schemas for each route type -export const deepLinkSchemas = { +export const deepLinkPathSchemas = { claim: z.object({ id: z.string(), }), @@ -60,7 +60,7 @@ export const deepLinkSchemas = { jwt: z.string().optional(), }), "onboard-meeting-members": z.object({ - id: z.string(), + groupId: z.string(), }), project: z.object({ id: z.string(), @@ -70,6 +70,17 @@ export const deepLinkSchemas = { }), }; +export const deepLinkQuerySchemas = { + "onboard-meeting-members": z.object({ + password: z.string(), + }), +}; + +// Add a union type of all valid route paths +export const VALID_DEEP_LINK_ROUTES = Object.keys( + deepLinkPathSchemas, +) as readonly (keyof typeof deepLinkPathSchemas)[]; + // Create a type from the array export type DeepLinkRoute = (typeof VALID_DEEP_LINK_ROUTES)[number]; @@ -80,14 +91,13 @@ export const baseUrlSchema = z.object({ queryParams: z.record(z.string()).optional(), }); -// Add a union type of all valid route paths -export const VALID_DEEP_LINK_ROUTES = Object.keys( - deepLinkSchemas, -) as readonly (keyof typeof deepLinkSchemas)[]; +// export type DeepLinkPathParams = { +// [K in keyof typeof deepLinkPathSchemas]: z.infer<(typeof deepLinkPathSchemas)[K]>; +// }; -export type DeepLinkParams = { - [K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>; -}; +// export type DeepLinkQueryParams = { +// [K in keyof typeof deepLinkQuerySchemas]: z.infer<(typeof deepLinkQuerySchemas)[K]>; +// }; export interface DeepLinkError extends Error { code: string; diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index d8445607..8d94185d 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -47,10 +47,11 @@ import { Router } from "vue-router"; import { z } from "zod"; import { - deepLinkSchemas, + deepLinkPathSchemas, baseUrlSchema, routeSchema, DeepLinkRoute, + deepLinkQuerySchemas, } from "../interfaces/deepLinks"; import type { DeepLinkError } from "../interfaces/deepLinks"; import { logger } from "../utils/logger"; @@ -74,7 +75,7 @@ function getFirstKeyFromZodObject( * because "router.replace" expects the right parameter name for the route. */ export const ROUTE_MAP: Record = - Object.entries(deepLinkSchemas).reduce( + Object.entries(deepLinkPathSchemas).reduce( (acc, [routeName, schema]) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const paramKey = getFirstKeyFromZodObject(schema as z.ZodObject); @@ -198,15 +199,22 @@ export class DeepLinkHandler { } // Continue with parameter validation as before... - const schema = deepLinkSchemas[path as keyof typeof deepLinkSchemas]; + const pathSchema = deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas]; + const querySchema = deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas]; - let validatedParams; + let validatedPathParams: Record = {}; + let validatedQueryParams: Record = {}; try { - validatedParams = await schema.parseAsync(params); + if (pathSchema) { + validatedPathParams = await pathSchema.parseAsync(params); + } + if (querySchema) { + validatedQueryParams = await querySchema.parseAsync(query); + } } catch (error) { // For parameter validation errors, provide specific error feedback logger.error( - `[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, + `[DeepLink] Invalid parameters for route name ${routeName} for path: ${path} ... with error: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, ); await this.router.replace({ name: "deep-link-error", @@ -226,20 +234,22 @@ export class DeepLinkHandler { try { await this.router.replace({ name: routeName, - params: validatedParams, + params: validatedPathParams, + query: validatedQueryParams }); } catch (error) { logger.error( - `[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)}`, + `[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedPathParams)} ... and query: ${JSON.stringify(validatedQueryParams)}`, ); // For parameter validation errors, provide specific error feedback await this.router.replace({ name: "deep-link-error", - params: validatedParams, + params: validatedPathParams, query: { originalPath: path, errorCode: "ROUTING_ERROR", errorMessage: `Error routing to ${routeName}: ${JSON.stringify(error)}`, + ...validatedQueryParams, }, }); } diff --git a/src/views/DeepLinkErrorView.vue b/src/views/DeepLinkErrorView.vue index 6decd859..a3b53b09 100644 --- a/src/views/DeepLinkErrorView.vue +++ b/src/views/DeepLinkErrorView.vue @@ -47,7 +47,7 @@ import { computed, onMounted } from "vue"; import { useRoute, useRouter } from "vue-router"; import { VALID_DEEP_LINK_ROUTES, - deepLinkSchemas, + deepLinkPathSchemas, } from "../interfaces/deepLinks"; import { logConsoleAndDb } from "../db/databaseUtil"; import { logger } from "../utils/logger"; @@ -56,7 +56,7 @@ const route = useRoute(); const router = useRouter(); // an object with the route as the key and the first param name as the value const deepLinkSchemaKeys = Object.fromEntries( - Object.entries(deepLinkSchemas).map(([route, schema]) => { + Object.entries(deepLinkPathSchemas).map(([route, schema]) => { const param = Object.keys(schema.shape)[0]; return [route, param]; }), diff --git a/src/views/OnboardMeetingMembersView.vue b/src/views/OnboardMeetingMembersView.vue index a1280011..7e42718b 100644 --- a/src/views/OnboardMeetingMembersView.vue +++ b/src/views/OnboardMeetingMembersView.vue @@ -113,7 +113,7 @@ export default class OnboardMeetingMembersView extends Vue { try { // Identity creation should be handled by router guard, but keep as fallback for meeting setup if (!this.activeDid) { - logger.info( + this.$logAndConsole( "[OnboardMeetingMembersView] No active DID found, creating identity as fallback for meeting setup", ); this.activeDid = await generateSaveAndActivateIdentity(); From b43ff58b71fc3aff4813354ec2b65da34aa7920d Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 18 Aug 2025 19:38:43 -0600 Subject: [PATCH 02/12] fix: Fix logging methods for iOS build. --- scripts/build-ios.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index e9009715..511358d5 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -173,20 +173,20 @@ check_ios_resources() { # Check for required assets if [ ! -f "assets/icon.png" ]; then - log_warning "App icon not found at assets/icon.png" + log_warn "App icon not found at assets/icon.png" fi if [ ! -f "assets/splash.png" ]; then - log_warning "Splash screen not found at assets/splash.png" + log_warn "Splash screen not found at assets/splash.png" fi # Check for iOS-specific files if [ ! -f "ios/App/App/Info.plist" ]; then - log_warning "Info.plist not found" + log_warn "Info.plist not found" fi if [ ! -f "ios/App/App/AppDelegate.swift" ]; then - log_warning "AppDelegate.swift not found" + log_warn "AppDelegate.swift not found" fi log_success "iOS resource check completed" From 01b2f9e8c13303267f3bb2e95574d49894c0ab4c Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 18 Aug 2025 20:19:55 -0600 Subject: [PATCH 03/12] chore: Bump to version 1.0.7 build 40. --- BUILDING.md | 71 +++++++++------------------ CHANGELOG.md | 2 +- android/app/build.gradle | 4 +- ios/App/App.xcodeproj/project.pbxproj | 8 +-- package-lock.json | 4 +- package.json | 2 +- 6 files changed, 32 insertions(+), 59 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index e1e94fcd..571fd6c2 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1017,47 +1017,27 @@ If you need to build manually or want to understand the individual steps: export GEM_PATH=$shortened_path ``` -1. Build the web assets & update ios: +1. Bump the version in package.json, then here. - ```bash - rm -rf dist - npm run build:web - npm run build:capacitor - npx cap sync ios - ``` - - - If that fails with "Could not find..." then look at the "gem_path" instructions above. - -3. Copy the assets: - - ```bash - # It makes no sense why capacitor-assets will not run without these but it actually changes the contents. - mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset - echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json - mkdir -p ios/App/App/Assets.xcassets/Splash.imageset - echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json - npx capacitor-assets generate --ios ``` - -4. Bump the version to match Android & package.json: - - ``` - cd ios/App && xcrun agvtool new-version 39 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.6;/g" App.xcodeproj/project.pbxproj && cd - + cd ios/App && xcrun agvtool new-version 40 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.7;/g" App.xcodeproj/project.pbxproj && cd - # Unfortunately this edits Info.plist directly. #xcrun agvtool new-marketing-version 0.4.5 ``` -5. Open the project in Xcode: +2. Build. - ```bash - npx cap open ios - ``` + Here's prod. Also available: test, dev + + ```bash + npm run build:ios:prod + ``` -6. Use Xcode to build and run on simulator or device. +3.1. Use Xcode to build and run on simulator or device. * Select Product -> Destination with some Simulator version. Then click the run arrow. -7. Release +3.2. Use Xcode to release. * Someday: Under "General" we want to rename a bunch of things to "Time Safari" * Choose Product -> Destination -> Any iOS Device @@ -1125,35 +1105,28 @@ The recommended way to build for Android is using the automated build script: #### Manual Build Process -1. Build the web assets: - - ```bash - rm -rf dist - npm run build:web - npm run build:capacitor - ``` - -2. Update Android project with latest build: +1. Bump the version in package.json, then here: android/app/build.gradle - ```bash - npx cap sync android - ``` + ```bash + perl -p -i -e 's/versionCode .*/versionCode 40/g' android/app/build.gradle + perl -p -i -e 's/versionName .*/versionName "1.0.7"/g' android/app/build.gradle + ``` -3. Copy the assets +2. Build. - ```bash - npx capacitor-assets generate --android - ``` + Here's prod. Also available: test, dev -4. Bump version to match iOS & package.json: android/app/build.gradle + ```bash + npm run build:android:prod + ``` -5. Open the project in Android Studio: +3. Open the project in Android Studio: ```bash npx cap open android ``` -6. Use Android Studio to build and run on emulator or device. +4. Use Android Studio to build and run on emulator or device. ## Android Build from the console diff --git a/CHANGELOG.md b/CHANGELOG.md index cf28e788..b16ef146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.0.3] - 2025.07.12 +## [1.0.7] - 2025.08.18 ### Changed - Photo is pinned to profile mode ### Fixed diff --git a/android/app/build.gradle b/android/app/build.gradle index a92af2db..57c34006 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -31,8 +31,8 @@ android { applicationId "app.timesafari.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 39 - versionName "1.0.6" + versionCode 40 + versionName "1.0.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 381f4bab..5b57160c 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -403,7 +403,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = GM3FS5JQPH; ENABLE_APP_SANDBOX = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -413,7 +413,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -430,7 +430,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = GM3FS5JQPH; ENABLE_APP_SANDBOX = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -440,7 +440,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; diff --git a/package-lock.json b/package-lock.json index d6914554..b152486e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.0.7", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", diff --git a/package.json b/package.json index bdb48dd0..c493198a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.0.7", "description": "Time Safari Application", "author": { "name": "Time Safari Team" From e6ce71362a3cd2a478bcfa04a8e059826ca0fd48 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 18 Aug 2025 20:26:05 -0600 Subject: [PATCH 04/12] chore: bump version and add "-beta" --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b152486e..f8c11390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "timesafari", - "version": "1.0.7", + "version": "1.0.8-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timesafari", - "version": "1.0.7", + "version": "1.0.8-beta", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", diff --git a/package.json b/package.json index c493198a..1c685c2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timesafari", - "version": "1.0.7", + "version": "1.0.8-beta", "description": "Time Safari Application", "author": { "name": "Time Safari Team" From b138f5cdaf0ab85896e20b366baffd4df1cbc74f Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 18 Aug 2025 20:37:15 -0600 Subject: [PATCH 05/12] doc: Fix BUILDING & CHANGELOG. --- BUILDING.md | 4 ++-- CHANGELOG.md | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 571fd6c2..e5abf069 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1159,9 +1159,9 @@ cd - At play.google.com/console: -- Go to the Testing Track (eg. Closed). +- Go to Production or the Closed Testing and either Create Track or Manage Track. - Click "Create new release". -- Upload the `aab` file. +- Upload the `aab` file from: app/build/outputs/bundle/release/app-release.aab - Hit "Next". - Save, go to "Publishing Overview" as prompted, and click "Send changes for review". diff --git a/CHANGELOG.md b/CHANGELOG.md index b16ef146..19209fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.0.7] - 2025.08.18 -### Changed -- Photo is pinned to profile mode ### Fixed -- Deep link URLs (and other prod settings) -- Error in BVC begin view +- Deep link for onboard-meeting-members + ## [1.0.6] - 2025.08.09 ### Fixed From d39e21394c044b058f1c2ed5d2753b153b816540 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 19 Aug 2025 03:36:22 +0000 Subject: [PATCH 06/12] refactor(rules): consolidate type safety content and clean software development ruleset - Remove duplicate content and restore file integrity in software_development.mdc - Add comprehensive Type Safety Enforcement section to type_safety_guide.mdc - Clean up file structure and eliminate corruption from duplicate sections - Move type safety patterns and guidelines to appropriate specialized guide --- .../rules/development/type_safety_guide.mdc | 17 +++ .cursor/rules/software_development.mdc | 118 +++++++----------- 2 files changed, 59 insertions(+), 76 deletions(-) diff --git a/.cursor/rules/development/type_safety_guide.mdc b/.cursor/rules/development/type_safety_guide.mdc index 507e3f23..6dba1416 100644 --- a/.cursor/rules/development/type_safety_guide.mdc +++ b/.cursor/rules/development/type_safety_guide.mdc @@ -40,6 +40,23 @@ Practical rules to keep TypeScript strict and predictable. Minimize exceptions. - Avoid `(obj as any)[k]`. +## Type Safety Enforcement + +### Core Type Safety Rules +- **No `any` Types**: Use explicit types or `unknown` with proper type guards +- **Error Handling Uses Guards**: Implement and reuse type guards from `src/interfaces/**` +- **Dynamic Property Access**: Use `keyof` + `in` checks for type-safe property access + +### Type Guard Patterns +- **API Errors**: Use `isApiError(error)` guards for API error handling +- **Database Errors**: Use `isDatabaseError(error)` guards for database operations +- **Axios Errors**: Implement `isAxiosError(error)` guards for HTTP error handling + +### Implementation Guidelines +- **Avoid Type Assertions**: Replace `as any` with proper type guards and interfaces +- **Narrow Types Properly**: Use type guards to narrow `unknown` types safely +- **Document Type Decisions**: Explain complex type structures and their purpose + ## Minimal Special Cases (document in PR when used) - **Vue refs / instances**: Use `ComponentPublicInstance` or specific component diff --git a/.cursor/rules/software_development.mdc b/.cursor/rules/software_development.mdc index f84bd5a2..745317cd 100644 --- a/.cursor/rules/software_development.mdc +++ b/.cursor/rules/software_development.mdc @@ -1,6 +1,3 @@ ---- -alwaysApply: true ---- # Software Development Ruleset @@ -89,90 +86,59 @@ Specialized guidelines for software development tasks including code review, deb - [ ] Solution complexity justified by evidence - [ ] Simpler alternatives considered and documented - [ ] Impact on existing systems assessed -# Software Development Ruleset +- [ ] Dependencies validated and accessible +- [ ] Environment impact assessed for team members +- [ ] Pre-build validation implemented where appropriate -## Purpose -Specialized guidelines for software development tasks including code review, debugging, architecture decisions, and testing. +## Additional Core Principles -## Core Principles +### 4. Dependency Management & Environment Validation +- **Pre-build Validation**: Always validate critical dependencies before executing build scripts +- **Environment Consistency**: Ensure team members have identical development environments +- **Dependency Verification**: Check that required packages are installed and accessible +- **Path Resolution**: Use `npx` for local dependencies to avoid PATH issues -### 1. Evidence-First Development -- **Code Citations Required**: Always cite specific file:line references when making claims -- **Execution Path Tracing**: Trace actual code execution before proposing architectural changes -- **Assumption Validation**: Flag assumptions as "assumed" vs "evidence-based" +## Additional Required Workflows -### 2. Code Review Standards -- **Trace Before Proposing**: Always trace execution paths before suggesting changes -- **Evidence Over Inference**: Prefer code citations over logical deductions -- **Scope Validation**: Confirm the actual scope of problems before proposing solutions +### Dependency Validation (Before Proposing Changes) +- [ ] **Dependency Validation**: Verify all required dependencies are available and accessible -### 3. Problem-Solution Validation -- **Problem Scope**: Does the solution address the actual problem? -- **Evidence Alignment**: Does the solution match the evidence? -- **Complexity Justification**: Is added complexity justified by real needs? -- **Alternative Analysis**: What simpler solutions were considered? +### Environment Impact Assessment (During Solution Design) +- [ ] **Environment Impact**: Assess how changes affect team member setups -## Required Workflows +## Additional Competence Hooks -### Before Proposing Changes -- [ ] **Code Path Tracing**: Map execution flow from entry to exit -- [ ] **Evidence Collection**: Gather specific code citations and logs -- [ ] **Assumption Surfacing**: Identify what's proven vs. inferred -- [ ] **Scope Validation**: Confirm the actual extent of the problem +### Dependency & Environment Management +- **"What dependencies does this feature require and are they properly declared?"** +- **"How will this change affect team member development environments?"** +- **"What validation can we add to catch dependency issues early?"** -### During Solution Design -- [ ] **Evidence Alignment**: Ensure solution addresses proven problems -- [ ] **Complexity Assessment**: Justify any added complexity -- [ ] **Alternative Evaluation**: Consider simpler approaches first -- [ ] **Impact Analysis**: Assess effects on existing systems +## Dependency Management Best Practices -## Software-Specific Competence Hooks +### Pre-build Validation +- **Check Critical Dependencies**: Validate essential tools before executing build scripts +- **Use npx for Local Dependencies**: Prefer `npx tsx` over direct `tsx` to avoid PATH issues +- **Environment Consistency**: Ensure all team members have identical dependency versions -### Evidence Validation -- **"What code path proves this claim?"** -- **"How does data actually flow through the system?"** -- **"What am I assuming vs. what can I prove?"** +### Common Pitfalls +- **Missing npm install**: Team members cloning without running `npm install` +- **PATH Issues**: Direct command execution vs. npm script execution differences +- **Version Mismatches**: Different Node.js/npm versions across team members -### Code Tracing -- **"What's the execution path from user action to system response?"** -- **"Which components actually interact in this scenario?"** -- **"Where does the data originate and where does it end up?"** +### Validation Strategies +- **Dependency Check Scripts**: Implement pre-build validation for critical dependencies +- **Environment Requirements**: Document and enforce minimum Node.js/npm versions +- **Onboarding Checklist**: Standardize team member setup procedures -### Architecture Decisions -- **"What evidence shows this change is necessary?"** -- **"What simpler solution could achieve the same goal?"** -- **"How does this change affect the existing system architecture?"** - -## Integration with Other Rulesets - -### With base_context.mdc -- Inherits generic competence principles -- Adds software-specific evidence requirements -- Maintains collaboration and learning focus - -### With research_diagnostic.mdc -- Enhances investigation with code path tracing -- Adds evidence validation to diagnostic workflow -- Strengthens problem identification accuracy - -## Usage Guidelines +### Error Messages and Guidance +- **Specific Error Context**: Provide clear guidance when dependency issues occur +- **Actionable Solutions**: Direct users to specific commands (`npm install`, `npm run check:dependencies`) +- **Environment Diagnostics**: Implement comprehensive environment validation tools -### When to Use This Ruleset -- Code reviews and architectural decisions -- Bug investigation and debugging -- Performance optimization -- Feature implementation planning -- Testing strategy development - -### When to Combine with Others -- **base_context + software_development**: General development tasks -- **research_diagnostic + software_development**: Technical investigations -- **All three**: Complex architectural decisions or major refactoring +### Build Script Enhancements +- **Early Validation**: Check dependencies before starting build processes +- **Graceful Degradation**: Continue builds when possible but warn about issues +- **Helpful Tips**: Remind users about dependency management best practices -## Self-Check (model, before responding) -- [ ] Code path traced and documented -- [ ] Evidence cited with specific file:line references -- [ ] Assumptions clearly flagged as proven vs. inferred -- [ ] Solution complexity justified by evidence -- [ ] Simpler alternatives considered and documented -- [ ] Impact on existing systems assessed +- **Narrow Types Properly**: Use type guards to narrow `unknown` types safely +- **Document Type Decisions**: Explain complex type structures and their purpose From bc1214e9db26d78aeae9b3a2de8ce55c1ad7691b Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 19 Aug 2025 03:36:57 +0000 Subject: [PATCH 07/12] feat(dev): enhance development environment and dependency management - Add comprehensive environment setup documentation to README.md - Add check:dependencies npm script for environment validation - Update build scripts to use npx for local dependencies - Enhance Android build script with dependency validation - Add new check-dependencies.sh script for environment diagnostics --- README.md | 27 +++++++++ package.json | 5 +- scripts/build-android.sh | 34 +++++++++++ scripts/check-dependencies.sh | 110 ++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100755 scripts/check-dependencies.sh diff --git a/README.md b/README.md index efc9b1ad..fc954fd5 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,33 @@ npm run assets:clean npm run build:native ``` +### Environment Setup & Dependencies + +Before building the application, ensure your development environment is properly +configured: + +```bash +# Install all dependencies (required first time and after updates) +npm install + +# Validate your development environment +npm run check:dependencies + +# Check prerequisites for testing +npm run test:prerequisites +``` + +**Common Issues & Solutions**: + +- **"tsx: command not found"**: Run `npm install` to install devDependencies +- **"capacitor-assets: command not found"**: Ensure `@capacitor/assets` is installed +- **Build failures**: Run `npm run check:dependencies` to diagnose environment issues + +**Required Versions**: +- Node.js: 18+ (LTS recommended) +- npm: 8+ (comes with Node.js) +- Platform-specific tools: Android Studio, Xcode (for mobile builds) + ### Platform Support - **Android**: Adaptive icons with foreground/background, monochrome support diff --git a/package.json b/package.json index 1c685c2b..e0548c68 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "type-check": "tsc --noEmit", "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js", "test:prerequisites": "node scripts/check-prerequisites.js", + "check:dependencies": "./scripts/check-dependencies.sh", "test:all": "npm run lint && tsc && npm run test:web && npm run test:mobile && ./scripts/test-safety-check.sh && echo '\n\n\nGotta add the performance tests'", "test:web": "npx playwright test -c playwright.config-local.ts --trace on", "test:mobile": "./scripts/test-mobile.sh", @@ -28,8 +29,8 @@ "build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts", "build:capacitor:sync": "npm run build:capacitor && npx cap sync", "build:native": "vite build && npx cap sync && npx capacitor-assets generate", - "assets:config": "tsx scripts/assets-config.ts", - "assets:validate": "tsx scripts/assets-validator.ts", + "assets:config": "npx tsx scripts/assets-config.ts", + "assets:validate": "npx tsx scripts/assets-validator.ts", "assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true", "build:ios": "./scripts/build-ios.sh", "build:ios:dev": "./scripts/build-ios.sh --dev", diff --git a/scripts/build-android.sh b/scripts/build-android.sh index c6c8ae88..c795a4b0 100755 --- a/scripts/build-android.sh +++ b/scripts/build-android.sh @@ -49,6 +49,31 @@ set -e # Source common utilities source "$(dirname "$0")/common.sh" +# Function to validate critical dependencies +validate_dependencies() { + log_info "Validating critical dependencies..." + + # Check if node_modules exists + if [ ! -d "node_modules" ]; then + log_error "node_modules directory not found. Please run 'npm install' first." + exit 1 + fi + + # Check if tsx is available + if [ ! -f "node_modules/.bin/tsx" ]; then + log_error "tsx dependency not found. Please run 'npm install' first." + exit 1 + fi + + # Check if capacitor-assets is available + if [ ! -f "node_modules/.bin/capacitor-assets" ]; then + log_error "capacitor-assets dependency not found. Please run 'npm install' first." + exit 1 + fi + + log_success "All critical dependencies validated successfully" +} + # Default values BUILD_MODE="development" BUILD_TYPE="debug" @@ -179,6 +204,11 @@ parse_android_args "$@" # Print build header print_header "TimeSafari Android Build Process" + +# Validate dependencies before proceeding +validate_dependencies + +# Log build start log_info "Starting Android build process at $(date)" log_info "Build mode: $BUILD_MODE" log_info "Build type: $BUILD_TYPE" @@ -257,6 +287,7 @@ fi # Step 1: Validate asset configuration safe_execute "Validating asset configuration" "npm run assets:validate" || { log_warn "Asset validation found issues, but continuing with build..." + log_info "If you encounter build failures, please run 'npm install' first to ensure all dependencies are available." } # Step 2: Clean Android app @@ -337,6 +368,9 @@ if [ "$OPEN_STUDIO" = true ]; then log_info "Android Studio: opened" fi +# Reminder about dependency management +log_info "💡 Tip: If you encounter dependency issues, run 'npm install' to ensure all packages are up to date." + print_footer "Android Build" # Exit with success diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh new file mode 100755 index 00000000..c8e14e8b --- /dev/null +++ b/scripts/check-dependencies.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# check-dependencies.sh +# Author: Matthew Raymer +# Date: 2025-08-19 +# Description: Dependency validation script for TimeSafari development environment +# This script checks for critical dependencies required for building the application. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +print_header "TimeSafari Dependency Validation" + +log_info "Checking development environment dependencies..." + +# Check Node.js version +if command -v node &> /dev/null; then + NODE_VERSION=$(node --version) + log_info "Node.js version: $NODE_VERSION" + + # Extract major version number + MAJOR_VERSION=$(echo $NODE_VERSION | sed 's/v\([0-9]*\)\..*/\1/') + if [ "$MAJOR_VERSION" -lt 18 ]; then + log_error "Node.js version $NODE_VERSION is too old. Please upgrade to Node.js 18 or later." + exit 1 + fi +else + log_error "Node.js is not installed. Please install Node.js 18 or later." + exit 1 +fi + +# Check npm version +if command -v npm &> /dev/null; then + NPM_VERSION=$(npm --version) + log_info "npm version: $NPM_VERSION" +else + log_error "npm is not installed. Please install npm." + exit 1 +fi + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + log_error "node_modules directory not found." + log_info "Please run: npm install" + exit 1 +fi + +# Check critical dependencies +log_info "Validating critical packages..." + +CRITICAL_DEPS=("tsx" "capacitor-assets" "vite") + +for dep in "${CRITICAL_DEPS[@]}"; do + if [ -f "node_modules/.bin/$dep" ]; then + log_success "✓ $dep found" + else + log_error "✗ $dep not found in node_modules/.bin" + log_info "This usually means the package wasn't installed properly." + log_info "Try running: npm install" + exit 1 + fi +done + +# Check TypeScript via npx +if npx tsc --version &> /dev/null; then + TSC_VERSION=$(npx tsc --version) + log_success "✓ TypeScript found: $TSC_VERSION" +else + log_error "✗ TypeScript not accessible via npx" + log_info "Try running: npm install" + exit 1 +fi + +# Check Capacitor CLI +if command -v npx &> /dev/null; then + if npx cap --version &> /dev/null; then + CAP_VERSION=$(npx cap --version) + log_success "✓ Capacitor CLI version: $CAP_VERSION" + else + log_error "✗ Capacitor CLI not accessible via npx" + log_info "Try running: npm install @capacitor/cli" + exit 1 + fi +else + log_error "npx is not available. Please ensure npm is properly installed." + exit 1 +fi + +# Check Android development tools +if command -v adb &> /dev/null; then + log_success "✓ Android Debug Bridge (adb) found" +else + log_warn "⚠ Android Debug Bridge (adb) not found" + log_info "This is only needed for Android development and testing." +fi + +if command -v gradle &> /dev/null; then + GRADLE_VERSION=$(gradle --version | head -n 1) + log_success "✓ Gradle found: $GRADLE_VERSION" +else + log_warn "⚠ Gradle not found in PATH" + log_info "This is only needed if building outside of Android Studio." +fi + +log_success "Dependency validation completed successfully!" +log_info "Your development environment is ready for TimeSafari development." + +print_footer "Dependency Validation" From 9384f0083a16e726e749a214cf50e06f58f35a9a Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 19 Aug 2025 03:37:20 +0000 Subject: [PATCH 08/12] refactor(types): improve type safety and eliminate type assertions - Replace type assertions with proper type guards in ProfileService - Add isAxiosError type guard and improve error handling - Clean up formatting and improve type safety in deepLinks service - Remove type assertions in AccountViewView Vue component - Improve code formatting and consistency across services --- src/services/ProfileService.ts | 51 +++++++++++++++++++------- src/services/deepLinks.ts | 8 +++-- src/views/AccountViewView.vue | 66 ++++++++++++++++++++-------------- 3 files changed, 84 insertions(+), 41 deletions(-) diff --git a/src/services/ProfileService.ts b/src/services/ProfileService.ts index bdb27f46..c1779c71 100644 --- a/src/services/ProfileService.ts +++ b/src/services/ProfileService.ts @@ -127,10 +127,10 @@ export class ProfileService { logger.debug("Attempting to delete profile for DID:", activeDid); logger.debug("Using partner API server:", this.partnerApiServer); logger.debug("Request headers:", headers); - + const url = `${this.partnerApiServer}/api/partner/userProfile`; logger.debug("DELETE request URL:", url); - + const response = await this.axios.delete(url, { headers }); if (response.status === 200 || response.status === 204) { @@ -140,20 +140,22 @@ export class ProfileService { logger.error("Unexpected response status when deleting profile:", { status: response.status, statusText: response.statusText, - data: response.data + data: response.data, }); - throw new Error(`Profile not deleted - HTTP ${response.status}: ${response.statusText}`); + throw new Error( + `Profile not deleted - HTTP ${response.status}: ${response.statusText}`, + ); } } catch (error) { if (this.isApiError(error) && error.response) { - const response = error.response as any; // Type assertion for error response + const response = error.response; logger.error("API error deleting profile:", { status: response.status, statusText: response.statusText, data: response.data, - url: (error as any).config?.url + url: this.getErrorUrl(error), }); - + // Handle specific HTTP status codes if (response.status === 204) { logger.debug("Profile deleted successfully (204 No Content)"); @@ -163,7 +165,11 @@ export class ProfileService { return true; // Consider this a success if profile doesn't exist } else if (response.status === 400) { logger.error("Bad request when deleting profile:", response.data); - throw new Error(`Profile deletion failed: ${response.data?.message || 'Bad request'}`); + const errorMessage = + typeof response.data === "string" + ? response.data + : response.data?.message || "Bad request"; + throw new Error(`Profile deletion failed: ${errorMessage}`); } else if (response.status === 401) { logger.error("Unauthorized to delete profile"); throw new Error("You are not authorized to delete this profile"); @@ -172,7 +178,7 @@ export class ProfileService { throw new Error("You are not allowed to delete this profile"); } } - + logger.error("Error deleting profile:", errorStringForLog(error)); handleApiError(error as AxiosError, "/api/partner/userProfile"); return false; @@ -244,11 +250,32 @@ export class ProfileService { /** * Type guard for API errors */ - private isApiError( - error: unknown, - ): error is { response?: { status?: number } } { + private isApiError(error: unknown): error is { + response?: { + status?: number; + statusText?: string; + data?: { message?: string } | string; + }; + } { return typeof error === "object" && error !== null && "response" in error; } + + /** + * Extract URL from AxiosError without type casting + */ + private getErrorUrl(error: unknown): string | undefined { + if (this.isAxiosError(error)) { + return error.config?.url; + } + return undefined; + } + + /** + * Type guard for AxiosError + */ + private isAxiosError(error: unknown): error is AxiosError { + return error instanceof AxiosError; + } } /** diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index 8d94185d..ee6095bb 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -199,8 +199,10 @@ export class DeepLinkHandler { } // Continue with parameter validation as before... - const pathSchema = deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas]; - const querySchema = deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas]; + const pathSchema = + deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas]; + const querySchema = + deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas]; let validatedPathParams: Record = {}; let validatedQueryParams: Record = {}; @@ -235,7 +237,7 @@ export class DeepLinkHandler { await this.router.replace({ name: routeName, params: validatedPathParams, - query: validatedQueryParams + query: validatedQueryParams, }); } catch (error) { logger.error( diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 1c38a8bb..f309fc6e 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -174,16 +174,18 @@ :aria-busy="loadingProfile || savingProfile" > -
- - - (Debug: {{ isMapReady ? 'Map Ready' : 'Map Loading' }}) -
+
+ + + (Debug: {{ isMapReady ? "Map Ready" : "Map Loading" }}) +

The location you choose will be shared with the world until you remove @@ -918,15 +920,18 @@ export default class AccountViewView extends Vue { created() { this.notify = createNotifyHelpers(this.$notify); - + // Fix Leaflet icon issues in modern bundlers // This prevents the "Cannot read properties of undefined (reading 'Default')" error if (L.Icon.Default) { - delete (L.Icon.Default.prototype as any)._getIconUrl; + delete (L.Icon.Default.prototype as { _getIconUrl?: unknown }) + ._getIconUrl; L.Icon.Default.mergeOptions({ - iconRetinaUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png', - iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png', - shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png', + iconRetinaUrl: + "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png", + iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png", + shadowUrl: + "https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png", }); } } @@ -955,7 +960,7 @@ export default class AccountViewView extends Vue { this.userProfileLatitude = profile.latitude; this.userProfileLongitude = profile.longitude; this.includeUserProfileLocation = profile.includeLocation; - + // Initialize map ready state if location is included if (profile.includeLocation) { this.isMapReady = false; // Will be set to true when map is ready @@ -1543,12 +1548,18 @@ export default class AccountViewView extends Vue { try { logger.debug("Map ready event fired, map object:", map); // doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup - const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2; + const zoom = + this.userProfileLatitude && this.userProfileLongitude ? 12 : 2; const lat = this.userProfileLatitude || 0; const lng = this.userProfileLongitude || 0; map.setView([lat, lng], zoom); this.isMapReady = true; - logger.debug("Map ready state set to true, coordinates:", [lat, lng], "zoom:", zoom); + logger.debug( + "Map ready state set to true, coordinates:", + [lat, lng], + "zoom:", + zoom, + ); } catch (error) { logger.error("Error in onMapReady:", error); this.isMapReady = true; // Set to true even on error to prevent infinite loading @@ -1560,7 +1571,7 @@ export default class AccountViewView extends Vue { // Check if map ref is available const mapRef = this.$refs.profileMap; logger.debug("Map ref:", mapRef); - + // Try to set map ready after component is mounted setTimeout(() => { this.isMapReady = true; @@ -1597,9 +1608,9 @@ export default class AccountViewView extends Vue { longitude: this.userProfileLongitude, includeLocation: this.includeUserProfileLocation, }; - + logger.debug("Saving profile data:", profileData); - + const success = await this.profileService.saveProfile( this.activeDid, profileData, @@ -1628,7 +1639,7 @@ export default class AccountViewView extends Vue { this.userProfileLatitude = updated.latitude; this.userProfileLongitude = updated.longitude; this.includeUserProfileLocation = updated.includeLocation; - + // Reset map ready state when toggling location if (!updated.includeLocation) { this.isMapReady = false; @@ -1679,7 +1690,7 @@ export default class AccountViewView extends Vue { } } catch (error) { logger.error("Error in deleteProfile component method:", error); - + // Show more specific error message if available if (error instanceof Error) { this.notify.error(error.message); @@ -1710,7 +1721,10 @@ export default class AccountViewView extends Vue { onLocationCheckboxChange(): void { try { - logger.debug("Location checkbox changed, new value:", this.includeUserProfileLocation); + logger.debug( + "Location checkbox changed, new value:", + this.includeUserProfileLocation, + ); if (!this.includeUserProfileLocation) { // Location checkbox was unchecked, clean up map state this.isMapReady = false; @@ -1721,7 +1735,7 @@ export default class AccountViewView extends Vue { // Location checkbox was checked, start map initialization timeout this.isMapReady = false; logger.debug("Location checked, starting map initialization timeout"); - + // Try to set map ready after a short delay to allow Vue to render setTimeout(() => { if (!this.isMapReady) { @@ -1729,7 +1743,7 @@ export default class AccountViewView extends Vue { this.isMapReady = true; } }, 1000); // 1 second delay - + this.handleMapInitFailure(); } } catch (error) { From 8724f8bbe0161ee86339177e46d8d3db7aaa77f3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 19 Aug 2025 03:41:30 +0000 Subject: [PATCH 09/12] fix: resolve CHANGELOG version mismatch and Android clean hanging issue - Fix CHANGELOG.md version from [1.0.7] to [1.0.8-beta] to match package.json - Replace problematic clean:android npm script with robust clean-android.sh script - Add timeout protection (30s) to prevent adb commands from hanging indefinitely - Include cross-platform timeout fallback using perl for macOS compatibility - Improve logging and error handling for Android cleanup process Fixes team member reported issues: - CHANGELOG version inconsistency - clean:android getting stuck during execution --- CHANGELOG.md | 2 +- package.json | 2 +- scripts/clean-android.sh | 62 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100755 scripts/clean-android.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 19209fb6..3c8febc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.0.7] - 2025.08.18 +## [1.0.8-beta] - 2025.08.18 ### Fixed - Deep link for onboard-meeting-members diff --git a/package.json b/package.json index e0548c68..cfe759b1 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "build:electron:dmg:dev": "./scripts/build-electron.sh --dev --dmg", "build:electron:dmg:test": "./scripts/build-electron.sh --test --dmg", "build:electron:dmg:prod": "./scripts/build-electron.sh --prod --dmg", - "clean:android": "adb uninstall app.timesafari.app || true", + "clean:android": "./scripts/clean-android.sh", "clean:ios": "rm -rf ios/App/build ios/App/Pods ios/App/output ios/App/App/public ios/DerivedData ios/capacitor-cordova-ios-plugins ios/App/App/capacitor.config.json ios/App/App/config.xml || true", "clean:electron": "./scripts/build-electron.sh --clean", "clean:all": "npm run clean:ios && npm run clean:android && npm run clean:electron", diff --git a/scripts/clean-android.sh b/scripts/clean-android.sh new file mode 100755 index 00000000..4fa354af --- /dev/null +++ b/scripts/clean-android.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# clean-android.sh +# Author: Matthew Raymer +# Date: 2025-08-19 +# Description: Clean Android app with timeout protection to prevent hanging +# This script safely uninstalls the TimeSafari app from connected Android devices +# with a 30-second timeout to prevent indefinite hanging. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Function to implement timeout for systems without timeout command +timeout_command() { + local timeout_seconds="$1" + shift + + # Check if timeout command exists + if command -v timeout &> /dev/null; then + timeout "$timeout_seconds" "$@" + else + # Fallback for systems without timeout (like macOS) + # Use perl to implement timeout + perl -e ' + eval { + local $SIG{ALRM} = sub { die "timeout" }; + alarm shift; + system @ARGV; + alarm 0; + }; + if ($@) { exit 1; } + ' "$timeout_seconds" "$@" + fi +} + +log_info "Starting Android cleanup process..." + +# Check if adb is available +if ! command -v adb &> /dev/null; then + log_error "adb command not found. Please install Android SDK Platform Tools." + exit 1 +fi + +# Check for connected devices +log_info "Checking for connected Android devices..." +if adb devices | grep -q 'device$'; then + log_info "Android device(s) found. Attempting to uninstall app..." + + # Try to uninstall with timeout + if timeout_command 30 adb uninstall app.timesafari.app; then + log_success "Successfully uninstalled TimeSafari app" + else + log_warn "Uninstall failed or timed out after 30 seconds" + log_info "This is normal if the app wasn't installed or device is unresponsive" + fi +else + log_info "No Android devices connected. Skipping uninstall." +fi + +log_success "Android cleanup process completed" From c80ded9e6dad79e0b99464c864b4a73cf61fe516 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Tue, 19 Aug 2025 19:43:24 -0600 Subject: [PATCH 10/12] fix: CHANGELOG version --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c8febc6..19209fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.0.8-beta] - 2025.08.18 +## [1.0.7] - 2025.08.18 ### Fixed - Deep link for onboard-meeting-members From fe08db1e95eeccb95ad796c79d50642e8b8b0bc6 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Tue, 19 Aug 2025 19:55:33 -0600 Subject: [PATCH 11/12] doc: Fix a remaining merge. --- .../rules/development/type_safety_guide.mdc | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/.cursor/rules/development/type_safety_guide.mdc b/.cursor/rules/development/type_safety_guide.mdc index faee146b..9c23938a 100644 --- a/.cursor/rules/development/type_safety_guide.mdc +++ b/.cursor/rules/development/type_safety_guide.mdc @@ -44,7 +44,7 @@ Practical rules to keep TypeScript strict and predictable. Minimize exceptions. ## Type Safety Enforcement ### Core Type Safety Rules -<<<<<<< HEAD + - **No `any` Types**: Use explicit types or `unknown` with proper type guards - **Error Handling Uses Guards**: Implement and reuse type guards from `src/interfaces/**` - **Dynamic Property Access**: Use `keyof` + `in` checks for type-safe property access @@ -58,31 +58,6 @@ Practical rules to keep TypeScript strict and predictable. Minimize exceptions. - **Avoid Type Assertions**: Replace `as any` with proper type guards and interfaces - **Narrow Types Properly**: Use type guards to narrow `unknown` types safely - **Document Type Decisions**: Explain complex type structures and their purpose -======= - -- **No `any` Types**: Use explicit types or `unknown` with proper type guards -- **Error Handling Uses Guards**: Implement and reuse type guards from - `src/interfaces/**` -- **Dynamic Property Access**: Use `keyof` + `in` checks for type-safe - property access - -### Type Guard Patterns - -- **API Errors**: Use `isApiError(error)` guards for API error handling -- **Database Errors**: Use `isDatabaseError(error)` guards for database - operations -- **Axios Errors**: Implement `isAxiosError(error)` guards for HTTP error - handling - -### Implementation Guidelines - -- **Avoid Type Assertions**: Replace `as any` with proper type guards and - interfaces -- **Narrow Types Properly**: Use type guards to narrow `unknown` types - safely -- **Document Type Decisions**: Explain complex type structures and their - purpose ->>>>>>> master ## Minimal Special Cases (document in PR when used) From 7d73e09de71a3fea31c52bced40e5065ce6585ad Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Tue, 19 Aug 2025 19:56:54 -0600 Subject: [PATCH 12/12] doc: Fix merge conflict. --- .cursor/rules/software_development.mdc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.cursor/rules/software_development.mdc b/.cursor/rules/software_development.mdc index 5eb25ac7..2f7c2c71 100644 --- a/.cursor/rules/software_development.mdc +++ b/.cursor/rules/software_development.mdc @@ -171,7 +171,6 @@ debugging, architecture decisions, and testing. - [ ] Environment impact assessed for team members - [ ] Pre-build validation implemented where appropriate -<<<<<<< HEAD ## Additional Core Principles ### 4. Dependency Management & Environment Validation @@ -224,12 +223,3 @@ debugging, architecture decisions, and testing. - **Narrow Types Properly**: Use type guards to narrow `unknown` types safely - **Document Type Decisions**: Explain complex type structures and their purpose -======= ---- - -**Status**: Active development guidelines -**Priority**: High -**Estimated Effort**: Ongoing -**Dependencies**: base_context.mdc, research_diagnostic.mdc -**Stakeholders**: Development team ->>>>>>> master