89 changed files with 8629 additions and 2341 deletions
@ -0,0 +1,14 @@ |
|||
--- |
|||
alwaysApply: true |
|||
--- |
|||
# Directive for Documentation Generation |
|||
|
|||
1. Produce a **small, focused set of documents** rather than an overwhelming volume. |
|||
2. Ensure the content is **maintainable and worth preserving**, so that humans |
|||
are motivated to keep it up to date. |
|||
3. Prioritize **educational value**: the documents must clearly explain the |
|||
workings of the system. |
|||
4. Avoid **shallow, generic, or filler explanations** often found in |
|||
AI-generated documentation. |
|||
5. Aim for **clarity, depth, and usefulness**, so readers gain genuine understanding. |
|||
6. Always check the local system date to determine current date. |
@ -0,0 +1,122 @@ |
|||
--- |
|||
alwaysApply: true |
|||
--- |
|||
# Directive: Peaceful Co-Existence with Developers |
|||
|
|||
## 1) Version-Control Ownership |
|||
|
|||
* **MUST NOT** run `git add`, `git commit`, or any write action. |
|||
* **MUST** leave staging/committing to the developer. |
|||
|
|||
## 2) Source of Truth for Commit Text |
|||
|
|||
* **MUST** derive messages **only** from: |
|||
|
|||
* files **staged** for commit (primary), and |
|||
* files **awaiting staging** (context). |
|||
* **MUST** use the **diffs** to inform content. |
|||
* **MUST NOT** invent changes or imply work not present in diffs. |
|||
|
|||
## 3) Mandatory Preview Flow |
|||
|
|||
* **ALWAYS** present, before any real commit: |
|||
|
|||
* file list + brief per-file notes, |
|||
* a **draft commit message** (copy-paste ready), |
|||
* nothing auto-applied. |
|||
|
|||
--- |
|||
|
|||
# Commit Message Format (Normative) |
|||
|
|||
## A. Subject Line (required) |
|||
|
|||
``` |
|||
<type>(<scope>)<!>: <summary> |
|||
``` |
|||
|
|||
* **type** (lowercase, Conventional Commits): `feat|fix|refactor|perf|docs|test|build|chore|ci|revert` |
|||
* **scope**: optional module/package/area (e.g., `api`, `ui/login`, `db`) |
|||
* **!**: include when a breaking change is introduced |
|||
* **summary**: imperative mood, ≤ 72 chars, no trailing period |
|||
|
|||
**Examples** |
|||
|
|||
* `fix(api): handle null token in refresh path` |
|||
* `feat(ui/login)!: require OTP after 3 failed attempts` |
|||
|
|||
## B. Body (optional, when it adds non-obvious value) |
|||
|
|||
* One blank line after subject. |
|||
* Wrap at \~72 chars. |
|||
* Explain **what** and **why**, not line-by-line “how”. |
|||
* Include brief notes like tests passing or TS/lint issues resolved **only if material**. |
|||
|
|||
**Body checklist** |
|||
|
|||
* [ ] Problem/symptom being addressed |
|||
* [ ] High-level approach or rationale |
|||
* [ ] Risks, tradeoffs, or follow-ups (if any) |
|||
|
|||
## C. Footer (optional) |
|||
|
|||
* Issue refs: `Closes #123`, `Refs #456` |
|||
* Breaking change (alternative to `!`): |
|||
`BREAKING CHANGE: <impact + migration note>` |
|||
* Authors: `Co-authored-by: Name <email>` |
|||
* Security: `CVE-XXXX-YYYY: <short note>` (if applicable) |
|||
|
|||
--- |
|||
|
|||
## Content Guidance |
|||
|
|||
### Include (when relevant) |
|||
|
|||
* Specific fixes/features delivered |
|||
* Symptoms/problems fixed |
|||
* Brief note that tests passed or TS/lint errors resolved |
|||
|
|||
### Avoid |
|||
|
|||
* Vague: *improved, enhanced, better* |
|||
* Trivialities: tiny docs, one-liners, pure lint cleanups (separate, focused commits if needed) |
|||
* Redundancy: generic blurbs repeated across files |
|||
* Multi-purpose dumps: keep commits **narrow and focused** |
|||
* Long explanations that good inline code comments already cover |
|||
|
|||
**Guiding Principle:** Let code and inline docs speak. Use commits to highlight what isn’t obvious. |
|||
|
|||
--- |
|||
|
|||
# Copy-Paste Templates |
|||
|
|||
## Minimal (no body) |
|||
|
|||
```text |
|||
<type>(<scope>): <summary> |
|||
``` |
|||
|
|||
## Standard (with body & footer) |
|||
|
|||
```text |
|||
<type>(<scope>)<!>: <summary> |
|||
|
|||
<why-this-change?> |
|||
<what-it-does?> |
|||
<risks-or-follow-ups?> |
|||
|
|||
Closes #<id> |
|||
BREAKING CHANGE: <impact + migration> |
|||
Co-authored-by: <Name> <email> |
|||
``` |
|||
|
|||
--- |
|||
|
|||
# Assistant Output Checklist (before showing the draft) |
|||
|
|||
* [ ] List changed files + 1–2 line notes per file |
|||
* [ ] Provide **one** focused draft message (subject/body/footer) |
|||
* [ ] Subject ≤ 72 chars, imperative mood, correct `type(scope)!` syntax |
|||
* [ ] Body only if it adds non-obvious value |
|||
* [ ] No invented changes; aligns strictly with diffs |
|||
* [ ] Render as a single copy-paste block for the developer |
File diff suppressed because it is too large
@ -0,0 +1,338 @@ |
|||
# Environment Variable Precedence and API Configuration |
|||
|
|||
**Date:** August 4, 2025 |
|||
**Author:** Matthew Raymer |
|||
|
|||
## Overview |
|||
|
|||
This document explains the order of precedence for environment variables in the |
|||
TimeSafari project, how `.env` files are used, and the API configuration scheme |
|||
for different environments. |
|||
|
|||
## Order of Precedence (Highest to Lowest) |
|||
|
|||
### 1. Shell Script Overrides (Highest Priority) |
|||
|
|||
Shell scripts can override environment variables for platform-specific needs: |
|||
|
|||
```bash |
|||
# scripts/common.sh - setup_build_env() |
|||
if [ "$BUILD_MODE" = "development" ]; then |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000" |
|||
fi |
|||
``` |
|||
|
|||
### 2. Platform-Specific Overrides (High Priority) |
|||
|
|||
Platform-specific build scripts can override for mobile development: |
|||
|
|||
```bash |
|||
# scripts/build-android.sh |
|||
if [ "$BUILD_MODE" = "development" ]; then |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000" |
|||
fi |
|||
``` |
|||
|
|||
### 3. Environment-Specific .env Files (Medium Priority) |
|||
|
|||
Environment-specific `.env` files provide environment-specific defaults: |
|||
|
|||
```bash |
|||
# .env.development, .env.test, .env.production |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 |
|||
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 |
|||
``` |
|||
|
|||
### 4. Fallback .env File (Low Priority) |
|||
|
|||
General `.env` file provides project-wide defaults: |
|||
|
|||
```bash |
|||
# .env (if exists) |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 |
|||
``` |
|||
|
|||
### 5. app.ts Constants (Lowest Priority - Fallback) |
|||
|
|||
Hardcoded constants in `src/constants/app.ts` provide safety nets: |
|||
|
|||
```typescript |
|||
export const DEFAULT_ENDORSER_API_SERVER = |
|||
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER || |
|||
AppString.PROD_ENDORSER_API_SERVER; |
|||
``` |
|||
|
|||
## Build Process Flow |
|||
|
|||
### 1. Shell Scripts Set Base Values |
|||
|
|||
```bash |
|||
# scripts/common.sh |
|||
setup_build_env() { |
|||
if [ "$BUILD_MODE" = "development" ]; then |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000" |
|||
fi |
|||
} |
|||
``` |
|||
|
|||
### 2. Platform-Specific Overrides |
|||
|
|||
```bash |
|||
# scripts/build-android.sh |
|||
if [ "$BUILD_MODE" = "development" ]; then |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000" |
|||
fi |
|||
``` |
|||
|
|||
### 3. Load .env Files |
|||
|
|||
```bash |
|||
# scripts/build-web.sh |
|||
local env_file=".env.$BUILD_MODE" # .env.development, .env.test, .env.production |
|||
if [ -f "$env_file" ]; then |
|||
load_env_file "$env_file" |
|||
fi |
|||
|
|||
# Fallback to .env |
|||
if [ -f ".env" ]; then |
|||
load_env_file ".env" |
|||
fi |
|||
``` |
|||
|
|||
### 4. Vite Processes Environment |
|||
|
|||
```typescript |
|||
// vite.config.common.mts |
|||
dotenv.config(); // Loads .env files |
|||
``` |
|||
|
|||
### 5. Application Uses Values |
|||
|
|||
```typescript |
|||
// src/constants/app.ts |
|||
export const DEFAULT_ENDORSER_API_SERVER = |
|||
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER || |
|||
AppString.PROD_ENDORSER_API_SERVER; |
|||
``` |
|||
|
|||
## API Configuration Scheme |
|||
|
|||
### Environment Configuration Summary |
|||
|
|||
| Environment | Endorser API (Claims) | Partner API | Image API | |
|||
|-------------|----------------------|-------------|-----------| |
|||
| **Development** | `http://localhost:3000` | `http://localhost:3000` | `https://image-api.timesafari.app` | |
|||
| **Test** | `https://test-api.endorser.ch` | `https://test-partner-api.endorser.ch` | `https://image-api.timesafari.app` | |
|||
| **Production** | `https://api.endorser.ch` | `https://partner-api.endorser.ch` | `https://image-api.timesafari.app` | |
|||
|
|||
### Mobile Development Overrides |
|||
|
|||
#### Android Development |
|||
|
|||
- **Emulator**: `http://10.0.2.2:3000` (Android emulator default) |
|||
- **Physical Device**: `http://{CUSTOM_IP}:3000` (Custom IP for physical device) |
|||
|
|||
#### iOS Development |
|||
|
|||
- **Simulator**: `http://localhost:3000` (iOS simulator default) |
|||
- **Physical Device**: `http://{CUSTOM_IP}:3000` (Custom IP for physical device) |
|||
|
|||
## .env File Structure |
|||
|
|||
### .env.development |
|||
```bash |
|||
# ========================================== |
|||
# DEVELOPMENT ENVIRONMENT CONFIGURATION |
|||
# ========================================== |
|||
# API Server Configuration: |
|||
# - Endorser API (Claims): Local development server |
|||
# - Partner API: Local development server (aligned with claims) |
|||
# - Image API: Test server (shared for development) |
|||
# ========================================== |
|||
|
|||
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. |
|||
|
|||
# iOS doesn't like spaces in the app title. |
|||
TIME_SAFARI_APP_TITLE="TimeSafari_Dev" |
|||
VITE_APP_SERVER=http://localhost:8080 |
|||
|
|||
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). |
|||
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F |
|||
|
|||
# API Servers (Development - Local) |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 |
|||
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000 |
|||
|
|||
# Image API (Test server for development) |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app |
|||
|
|||
# Push Server (disabled for localhost) |
|||
#VITE_DEFAULT_PUSH_SERVER... can't be set up with localhost domain |
|||
|
|||
# Feature Flags |
|||
VITE_PASSKEYS_ENABLED=true |
|||
``` |
|||
|
|||
### .env.test |
|||
```bash |
|||
# ========================================== |
|||
# TEST ENVIRONMENT CONFIGURATION |
|||
# ========================================== |
|||
# API Server Configuration: |
|||
# - Endorser API (Claims): Test server |
|||
# - Partner API: Test server (aligned with claims) |
|||
# - Image API: Test server |
|||
# ========================================== |
|||
|
|||
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. |
|||
|
|||
# iOS doesn't like spaces in the app title. |
|||
TIME_SAFARI_APP_TITLE="TimeSafari_Test" |
|||
VITE_APP_SERVER=https://test.timesafari.app |
|||
|
|||
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). |
|||
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F |
|||
|
|||
# API Servers (Test Environment) |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch |
|||
|
|||
# Image API (Test server) |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app |
|||
|
|||
# Push Server (Test) |
|||
VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app |
|||
|
|||
# Feature Flags |
|||
VITE_PASSKEYS_ENABLED=true |
|||
``` |
|||
|
|||
### .env.production |
|||
```bash |
|||
# ========================================== |
|||
# PRODUCTION ENVIRONMENT CONFIGURATION |
|||
# ========================================== |
|||
# API Server Configuration: |
|||
# - Endorser API (Claims): Production server |
|||
# - Partner API: Production server (aligned with claims) |
|||
# - Image API: Production server |
|||
# ========================================== |
|||
|
|||
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue. |
|||
|
|||
# App Server |
|||
VITE_APP_SERVER=https://timesafari.app |
|||
|
|||
# This is the claim ID for actions in the BVC project. |
|||
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01GXYPFF7FA03NXKPYY142PY4H |
|||
|
|||
# API Servers (Production Environment) |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch |
|||
|
|||
# Image API (Production server) |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app |
|||
|
|||
# Push Server (Production) |
|||
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app |
|||
``` |
|||
|
|||
## Key Principles |
|||
|
|||
### 1. API Alignment |
|||
- **Partner API** values follow the same pattern as **Claim API** (Endorser API) |
|||
- Both APIs use the same environment-specific endpoints |
|||
- This ensures consistency across the application |
|||
|
|||
### 2. Platform Flexibility |
|||
- Shell scripts can override for platform-specific needs |
|||
- Android emulator uses `10.0.2.2:3000` |
|||
- iOS simulator uses `localhost:3000` |
|||
- Physical devices use custom IP addresses |
|||
|
|||
### 3. Environment Isolation |
|||
- Each environment has its own `.env` file |
|||
- Test environment uses test APIs |
|||
- Development environment uses local APIs |
|||
- Production environment uses production APIs |
|||
|
|||
### 4. Safety Nets |
|||
- Hardcoded constants in `app.ts` provide fallbacks |
|||
- Multiple layers of configuration prevent failures |
|||
- Clear precedence order ensures predictable behavior |
|||
|
|||
## Usage Examples |
|||
|
|||
### Development Build |
|||
```bash |
|||
# Uses .env.development + shell script overrides |
|||
npm run build:web -- --mode development |
|||
``` |
|||
|
|||
### Test Build |
|||
```bash |
|||
# Uses .env.test + shell script overrides |
|||
npm run build:web -- --mode test |
|||
``` |
|||
|
|||
### Production Build |
|||
```bash |
|||
# Uses .env.production + shell script overrides |
|||
npm run build:web -- --mode production |
|||
``` |
|||
|
|||
### Android Development |
|||
```bash |
|||
# Uses .env.development + Android-specific overrides |
|||
./scripts/build-android.sh --dev |
|||
``` |
|||
|
|||
### iOS Development |
|||
```bash |
|||
# Uses .env.development + iOS-specific overrides |
|||
./scripts/build-ios.sh --dev |
|||
``` |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Environment Variable Debugging |
|||
```bash |
|||
# Show current environment variables |
|||
./scripts/build-web.sh --env |
|||
|
|||
# Check specific variable |
|||
echo $VITE_DEFAULT_ENDORSER_API_SERVER |
|||
``` |
|||
|
|||
### Common Issues |
|||
|
|||
1. **Wrong API Server**: Check if shell script overrides are correct |
|||
2. **Missing .env File**: Ensure environment-specific .env file exists |
|||
3. **Platform-Specific Issues**: Verify platform overrides in build scripts |
|||
4. **Vite Not Loading**: Check if `dotenv.config()` is called |
|||
|
|||
### Validation |
|||
```bash |
|||
# Validate environment configuration |
|||
npm run test-env |
|||
``` |
|||
|
|||
## Best Practices |
|||
|
|||
1. **Always use environment-specific .env files** for different environments |
|||
2. **Keep shell script overrides minimal** and platform-specific |
|||
3. **Document API alignment** in .env file headers |
|||
4. **Use hardcoded fallbacks** in `app.ts` for safety |
|||
5. **Test all environments** before deployment |
|||
6. **Validate configuration** with test scripts |
|||
|
|||
## Related Documentation |
|||
|
|||
- [Build System Overview](../build-system/README.md) |
|||
- [Android Custom API IP](../platforms/android-custom-api-ip.md) |
|||
- [API Configuration](../api-configuration.md) |
|||
- [Environment Setup](../environment-setup.md) |
@ -0,0 +1,322 @@ |
|||
# Mobile Custom API IP Configuration |
|||
|
|||
**Author**: Matthew Raymer |
|||
**Date**: 2025-01-27 |
|||
**Status**: ✅ **COMPLETE** - Custom API IP support for physical device development |
|||
|
|||
## Overview |
|||
|
|||
When deploying TimeSafari to physical Android devices during development, you may need to specify a custom IP address for the claim API server. This is necessary because physical devices cannot access `localhost` or `10.0.2.2` (Android emulator IP) to reach your local development server. |
|||
|
|||
## Problem |
|||
|
|||
During mobile development: |
|||
- **Android Emulator**: Uses `10.0.2.2:3000` to access host machine's localhost (Android emulator default) |
|||
- **iOS Simulator**: Uses `localhost:3000` to access host machine's localhost (iOS simulator default) |
|||
- **Physical Devices**: Cannot access `localhost` or `10.0.2.2` - needs actual IP address for network access |
|||
|
|||
## Solution |
|||
|
|||
The mobile build system uses platform-appropriate defaults and supports specifying a custom IP address for the claim API server when building for physical devices: |
|||
- **Android**: Defaults to `10.0.2.2:3000` for emulator development |
|||
- **iOS**: Uses Capacitor default (`localhost:3000`) for simulator development |
|||
|
|||
## Usage |
|||
|
|||
### Command Line Usage |
|||
|
|||
```bash |
|||
# Android - Default behavior (uses 10.0.2.2 for emulator) |
|||
./scripts/build-android.sh --dev |
|||
|
|||
# Android - Custom IP for physical device |
|||
./scripts/build-android.sh --dev --api-ip 192.168.1.100 |
|||
|
|||
# iOS - Default behavior (uses localhost for simulator) |
|||
./scripts/build-ios.sh --dev |
|||
|
|||
# iOS - Custom IP for physical device |
|||
./scripts/build-ios.sh --dev --api-ip 192.168.1.100 |
|||
|
|||
# Test environment with custom IP |
|||
./scripts/build-android.sh --test --api-ip 192.168.1.100 |
|||
./scripts/build-ios.sh --test --api-ip 192.168.1.100 |
|||
|
|||
# Build and auto-run with custom IP |
|||
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --auto-run |
|||
./scripts/build-ios.sh --dev --api-ip 192.168.1.100 --auto-run |
|||
``` |
|||
|
|||
### NPM Scripts |
|||
|
|||
```bash |
|||
# Android - Default development build (uses 10.0.2.2 for emulator) |
|||
npm run build:android:dev |
|||
|
|||
# Android - Development build with custom IP (requires IP parameter) |
|||
npm run build:android:dev:custom 192.168.1.100 |
|||
|
|||
# iOS - Default development build (uses localhost for simulator) |
|||
npm run build:ios:dev |
|||
|
|||
# iOS - Development build with custom IP (requires IP parameter) |
|||
npm run build:ios:dev:custom 192.168.1.100 |
|||
|
|||
# Test builds with custom IP (requires IP parameter) |
|||
npm run build:android:test:custom 192.168.1.100 |
|||
npm run build:ios:test:custom 192.168.1.100 |
|||
|
|||
# Development build + auto-run with custom IP |
|||
npm run build:android:dev:run:custom 192.168.1.100 |
|||
npm run build:ios:dev:run:custom 192.168.1.100 |
|||
|
|||
# Test build + auto-run with custom IP |
|||
npm run build:android:test:run:custom 192.168.1.100 |
|||
npm run build:ios:test:run:custom 192.168.1.100 |
|||
``` |
|||
|
|||
## Examples |
|||
|
|||
### Scenario 1: Development on Simulator/Emulator (Default) |
|||
|
|||
```bash |
|||
# Android - Default behavior - uses 10.0.2.2 for emulator |
|||
npm run build:android:dev |
|||
|
|||
# iOS - Default behavior - uses localhost for simulator |
|||
npm run build:ios:dev |
|||
|
|||
# Build and immediately run on simulator/emulator |
|||
npm run build:android:dev:run |
|||
npm run build:ios:dev:run |
|||
``` |
|||
|
|||
### Scenario 2: Development on Physical Device |
|||
|
|||
```bash |
|||
# Your development server is running on 192.168.1.50:3000 |
|||
npm run build:android:dev:custom 192.168.1.50 |
|||
npm run build:ios:dev:custom 192.168.1.50 |
|||
|
|||
# Build and immediately run on device |
|||
npm run build:android:dev:run:custom 192.168.1.50 |
|||
npm run build:ios:dev:run:custom 192.168.1.50 |
|||
``` |
|||
|
|||
### Scenario 3: Testing on Physical Device |
|||
|
|||
```bash |
|||
# Your test server is running on 192.168.1.75:3000 |
|||
npm run build:android:test:custom 192.168.1.75 |
|||
npm run build:ios:test:custom 192.168.1.75 |
|||
|
|||
# Build and immediately run on device |
|||
npm run build:android:test:run:custom 192.168.1.75 |
|||
npm run build:ios:test:run:custom 192.168.1.75 |
|||
``` |
|||
|
|||
### Scenario 4: Direct Script Usage |
|||
|
|||
```bash |
|||
# Default behavior (uses platform-appropriate defaults) |
|||
./scripts/build-android.sh --dev --studio |
|||
./scripts/build-ios.sh --dev --studio |
|||
|
|||
# Custom IP for physical device |
|||
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --studio |
|||
./scripts/build-ios.sh --dev --api-ip 192.168.1.100 --studio |
|||
``` |
|||
|
|||
## How It Works |
|||
|
|||
### Environment Variable Override |
|||
|
|||
The build system handles API server configuration as follows: |
|||
|
|||
1. **Android default**: Uses Android emulator default (`http://10.0.2.2:3000`) |
|||
2. **iOS default**: Uses Capacitor default (`http://localhost:3000`) |
|||
3. **Custom IP specified**: Overrides with `http://<custom-ip>:3000` for physical device development |
|||
4. **Maintains other APIs**: Image and Partner APIs remain at production URLs |
|||
5. **Logs the configuration**: Shows which IP is being used in build logs |
|||
|
|||
### Build Process |
|||
|
|||
```bash |
|||
# Development mode with Android emulator default (10.0.2.2) |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000" |
|||
npm run build:capacitor -- --mode development |
|||
|
|||
# Development mode with iOS simulator default (localhost) |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000" |
|||
npm run build:capacitor -- --mode development |
|||
|
|||
# Development mode with custom IP |
|||
export VITE_DEFAULT_ENDORSER_API_SERVER="http://192.168.1.100:3000" |
|||
export VITE_DEFAULT_PARTNER_API_SERVER="http://192.168.1.100:3000" |
|||
npm run build:capacitor -- --mode development |
|||
``` |
|||
|
|||
### Default Behavior |
|||
|
|||
- **Android (no `--api-ip`)**: Uses Android emulator default (`10.0.2.2:3000`) |
|||
- **iOS (no `--api-ip`)**: Uses Capacitor default (`localhost:3000`) |
|||
- **Custom IP specified**: Uses provided IP address for physical device development |
|||
- **Invalid IP format**: Build will fail with clear error message |
|||
- **Network unreachable**: App will show connection errors at runtime |
|||
|
|||
## Finding Your IP Address |
|||
|
|||
### On Linux/macOS |
|||
|
|||
```bash |
|||
# Find your local IP address |
|||
ifconfig | grep "inet " | grep -v 127.0.0.1 |
|||
# or |
|||
ip addr show | grep "inet " | grep -v 127.0.0.1 |
|||
``` |
|||
|
|||
### On Windows |
|||
|
|||
```bash |
|||
# Find your local IP address |
|||
ipconfig | findstr "IPv4" |
|||
``` |
|||
|
|||
### Common Network Patterns |
|||
|
|||
- **Home WiFi**: Usually `192.168.1.x` or `192.168.0.x` |
|||
- **Office Network**: May be `10.x.x.x` or `172.16.x.x` |
|||
- **Mobile Hotspot**: Often `192.168.43.x` |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Common Issues |
|||
|
|||
#### 1. Device Cannot Connect to API |
|||
|
|||
```bash |
|||
# Check if your IP is accessible |
|||
ping 192.168.1.100 |
|||
|
|||
# Check if port 3000 is open |
|||
telnet 192.168.1.100 3000 |
|||
``` |
|||
|
|||
#### 2. Build Fails with Invalid IP |
|||
|
|||
```bash |
|||
# Ensure IP format is correct |
|||
./scripts/build-android.sh --dev --api-ip 192.168.1.100 # ✅ Correct |
|||
./scripts/build-android.sh --dev --api-ip localhost # ❌ Wrong |
|||
``` |
|||
|
|||
#### 3. Firewall Blocking Connection |
|||
|
|||
```bash |
|||
# Check firewall settings |
|||
sudo ufw status # Ubuntu/Debian |
|||
sudo firewall-cmd --list-all # CentOS/RHEL |
|||
``` |
|||
|
|||
### Debug Mode |
|||
|
|||
```bash |
|||
# Enable verbose logging |
|||
./scripts/build-android.sh --dev --api-ip 192.168.1.100 --verbose |
|||
``` |
|||
|
|||
## Best Practices |
|||
|
|||
### 1. Use Consistent IP Addresses |
|||
|
|||
```bash |
|||
# Create aliases for common development scenarios |
|||
alias build-dev="npm run build:android:dev:custom 192.168.1.100" |
|||
alias build-test="npm run build:android:test:custom 192.168.1.100" |
|||
``` |
|||
|
|||
### 2. Document Your Setup |
|||
|
|||
```bash |
|||
# Create a development setup file |
|||
echo "DEV_API_IP=192.168.1.100" > .env.development |
|||
echo "TEST_API_IP=192.168.1.100" >> .env.development |
|||
``` |
|||
|
|||
### 3. Network Security |
|||
|
|||
- Ensure your development server is only accessible on your local network |
|||
- Use HTTPS in production environments |
|||
- Consider VPN for remote development scenarios |
|||
|
|||
### 4. Team Development |
|||
|
|||
```bash |
|||
# Share IP configuration with team |
|||
# Add to .env.example |
|||
DEV_API_IP=192.168.1.100 |
|||
TEST_API_IP=192.168.1.100 |
|||
``` |
|||
|
|||
## Integration with CI/CD |
|||
|
|||
### Environment Variables |
|||
|
|||
```yaml |
|||
# Example CI/CD configuration |
|||
variables: |
|||
DEV_API_IP: "192.168.1.100" |
|||
TEST_API_IP: "192.168.1.100" |
|||
|
|||
build: |
|||
script: |
|||
- npm run build:android:dev:custom $DEV_API_IP |
|||
``` |
|||
|
|||
### Automated Testing |
|||
|
|||
```bash |
|||
# Test with different IP configurations |
|||
npm run build:android:test:custom 192.168.1.100 |
|||
npm run build:android:test:custom 10.0.0.100 |
|||
``` |
|||
|
|||
## Migration from Legacy |
|||
|
|||
### Previous Workarounds |
|||
|
|||
Before this feature, developers had to: |
|||
1. Manually edit environment files |
|||
2. Use different build configurations |
|||
3. Modify source code for IP addresses |
|||
|
|||
### New Approach |
|||
|
|||
```bash |
|||
# Simple one-liner |
|||
npm run build:android:dev:custom 192.168.1.100 |
|||
``` |
|||
|
|||
## Future Enhancements |
|||
|
|||
### Planned Features |
|||
|
|||
1. **IP Validation**: Automatic IP format validation |
|||
2. **Network Discovery**: Auto-detect available IP addresses |
|||
3. **Port Configuration**: Support for custom ports |
|||
4. **Multiple APIs**: Support for custom IPs for all API endpoints |
|||
|
|||
### Integration Opportunities |
|||
|
|||
1. **Docker Integration**: Automatic IP detection in containerized environments |
|||
2. **Network Profiles**: Save and reuse common network configurations |
|||
3. **Hot Reload**: Automatic rebuild when IP changes |
|||
|
|||
--- |
|||
|
|||
**Status**: Complete and ready for production use |
|||
**Last Updated**: 2025-01-27 |
|||
**Version**: 1.0 |
|||
**Maintainer**: Matthew Raymer |
@ -0,0 +1,84 @@ |
|||
# Contact Sharing - URL Solution |
|||
|
|||
## Overview |
|||
|
|||
Simple implementation to switch ContactQRScanShowView from copying QR value (CSV) to copying a URL for better user experience. |
|||
|
|||
## Problem |
|||
|
|||
The ContactQRScanShowView was copying QR value (CSV content) to clipboard instead of a URL, making contact sharing less user-friendly. |
|||
|
|||
## Solution |
|||
|
|||
Updated the `onCopyUrlToClipboard()` method in ContactQRScanShowView.vue to generate and copy a URL instead of the QR value. |
|||
|
|||
## Changes Made |
|||
|
|||
### ContactQRScanShowView.vue |
|||
|
|||
**Added Imports:** |
|||
```typescript |
|||
import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer"; |
|||
import { Account } from "@/db/tables/accounts"; |
|||
``` |
|||
|
|||
**Updated Method:** |
|||
```typescript |
|||
async onCopyUrlToClipboard() { |
|||
try { |
|||
// Generate URL for sharing |
|||
const account = (await libsUtil.retrieveFullyDecryptedAccount( |
|||
this.activeDid, |
|||
)) as Account; |
|||
const jwtUrl = await generateEndorserJwtUrlForAccount( |
|||
account, |
|||
this.isRegistered, |
|||
this.givenName, |
|||
this.profileImageUrl, |
|||
true, |
|||
); |
|||
|
|||
// Copy the URL to clipboard |
|||
useClipboard() |
|||
.copy(jwtUrl) |
|||
.then(() => { |
|||
this.notify.toast( |
|||
"Copied", |
|||
NOTIFY_QR_URL_COPIED.message, |
|||
QR_TIMEOUT_MEDIUM, |
|||
); |
|||
}); |
|||
} catch (error) { |
|||
logger.error("Failed to generate contact URL:", error); |
|||
this.notify.error("Failed to generate contact URL. Please try again."); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Benefits |
|||
|
|||
1. **Better UX**: Recipients can click the URL to add contact directly |
|||
2. **Consistency**: Both ContactQRScanShowView and ContactQRScanFullView now use URL format |
|||
3. **Error Handling**: Graceful fallback if URL generation fails |
|||
4. **Simple**: Minimal changes, no new components needed |
|||
|
|||
## User Experience |
|||
|
|||
**Before:** |
|||
- Click QR code → Copy CSV data to clipboard |
|||
- Recipient must paste CSV into input field |
|||
|
|||
**After:** |
|||
- Click QR code → Copy URL to clipboard |
|||
- Recipient clicks URL → Contact added automatically |
|||
|
|||
## Testing |
|||
|
|||
- ✅ Linting passes |
|||
- ✅ Error handling implemented |
|||
- ✅ Consistent with ContactQRScanFullView behavior |
|||
- ✅ Maintains existing notification system |
|||
|
|||
## Deployment |
|||
|
|||
Ready for deployment. No breaking changes, maintains backward compatibility. |
File diff suppressed because it is too large
@ -0,0 +1,113 @@ |
|||
# $updateSettings to $saveSettings Consolidation Plan |
|||
|
|||
## Overview |
|||
Consolidate `$updateSettings` method into `$saveSettings` to eliminate code duplication and improve maintainability. The `$updateSettings` method is currently just a thin wrapper around `$saveSettings` and `$saveUserSettings`, providing no additional functionality. |
|||
|
|||
## Current State Analysis |
|||
|
|||
### Current Implementation |
|||
```typescript |
|||
// Current $updateSettings - just a wrapper |
|||
async $updateSettings(changes: Partial<Settings>, did?: string): Promise<boolean> { |
|||
try { |
|||
if (did) { |
|||
return await this.$saveUserSettings(did, changes); |
|||
} else { |
|||
return await this.$saveSettings(changes); |
|||
} |
|||
} catch (error) { |
|||
logger.error("[PlatformServiceMixin] Error updating settings:", error); |
|||
return false; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Usage Statistics |
|||
- **$updateSettings**: 42 references across codebase |
|||
- **$saveSettings**: 38 references across codebase |
|||
- **$saveUserSettings**: 12 references across codebase |
|||
|
|||
## Migration Strategy |
|||
|
|||
### Phase 1: Documentation and Planning ✅ |
|||
- [x] Document current usage patterns |
|||
- [x] Identify all call sites |
|||
- [x] Create migration plan |
|||
|
|||
### Phase 2: Implementation |
|||
- [ ] Update `$saveSettings` to accept optional `did` parameter |
|||
- [ ] Add error handling to `$saveSettings` (currently missing) |
|||
- [ ] Deprecate `$updateSettings` with migration notice |
|||
- [ ] Update all call sites to use `$saveSettings` directly |
|||
|
|||
### Phase 3: Cleanup |
|||
- [ ] Remove `$updateSettings` method |
|||
- [ ] Update documentation |
|||
- [ ] Update tests |
|||
|
|||
## Implementation Details |
|||
|
|||
### Enhanced $saveSettings Method |
|||
```typescript |
|||
async $saveSettings(changes: Partial<Settings>, did?: string): Promise<boolean> { |
|||
try { |
|||
// Convert settings for database storage |
|||
const convertedChanges = this._convertSettingsForStorage(changes); |
|||
|
|||
if (did) { |
|||
// User-specific settings |
|||
return await this.$saveUserSettings(did, convertedChanges); |
|||
} else { |
|||
// Default settings |
|||
return await this.$saveSettings(convertedChanges); |
|||
} |
|||
} catch (error) { |
|||
logger.error("[PlatformServiceMixin] Error saving settings:", error); |
|||
return false; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Migration Benefits |
|||
1. **Reduced Code Duplication**: Single method handles both use cases |
|||
2. **Improved Maintainability**: One place to fix issues |
|||
3. **Consistent Error Handling**: Unified error handling approach |
|||
4. **Better Type Safety**: Single method signature to maintain |
|||
|
|||
### Risk Assessment |
|||
- **Low Risk**: `$updateSettings` is just a wrapper, no complex logic |
|||
- **Backward Compatible**: Can maintain both methods during transition |
|||
- **Testable**: Existing tests can be updated incrementally |
|||
|
|||
## Call Site Migration Examples |
|||
|
|||
### Before (using $updateSettings) |
|||
```typescript |
|||
await this.$updateSettings({ searchBoxes: [newSearchBox] }); |
|||
await this.$updateSettings({ filterFeedByNearby: false }, userDid); |
|||
``` |
|||
|
|||
### After (using $saveSettings) |
|||
```typescript |
|||
await this.$saveSettings({ searchBoxes: [newSearchBox] }); |
|||
await this.$saveSettings({ filterFeedByNearby: false }, userDid); |
|||
``` |
|||
|
|||
## Testing Strategy |
|||
1. **Unit Tests**: Update existing tests to use `$saveSettings` |
|||
2. **Integration Tests**: Verify both default and user-specific settings work |
|||
3. **Migration Tests**: Ensure searchBoxes conversion still works |
|||
4. **Performance Tests**: Verify no performance regression |
|||
|
|||
## Timeline |
|||
- **Phase 1**: ✅ Complete |
|||
- **Phase 2**: 1-2 days |
|||
- **Phase 3**: 1 day |
|||
- **Total**: 2-3 days |
|||
|
|||
## Success Criteria |
|||
- [ ] All existing functionality preserved |
|||
- [ ] No performance regression |
|||
- [ ] All tests passing |
|||
- [ ] Reduced code duplication |
|||
- [ ] Improved maintainability |
@ -0,0 +1,207 @@ |
|||
# GiftedDialog Entity Types Refactoring |
|||
|
|||
## Overview |
|||
|
|||
This refactoring simplifies the `GiftedDialog` component by replacing the complex `updateEntityTypes()` method with explicit props for entity types. This makes the component more declarative, reusable, and easier to understand. |
|||
|
|||
## Problem |
|||
|
|||
The original `updateEntityTypes()` method used multiple props (`showProjects`, `fromProjectId`, `toProjectId`, `recipientEntityTypeOverride`) to determine entity types through complex conditional logic: |
|||
|
|||
```typescript |
|||
updateEntityTypes() { |
|||
// Reset and set entity types based on current context |
|||
this.giverEntityType = "person"; |
|||
this.recipientEntityType = "person"; |
|||
|
|||
// If recipient entity type is explicitly overridden, use that |
|||
if (this.recipientEntityTypeOverride) { |
|||
this.recipientEntityType = this.recipientEntityTypeOverride; |
|||
} |
|||
|
|||
// Determine entity types based on current context |
|||
if (this.showProjects) { |
|||
// HomeView "Project" button or ProjectViewView "Given by This" |
|||
this.giverEntityType = "project"; |
|||
// Only override recipient if not already set by recipientEntityTypeOverride |
|||
if (!this.recipientEntityTypeOverride) { |
|||
this.recipientEntityType = "person"; |
|||
} |
|||
} else if (this.fromProjectId) { |
|||
// ProjectViewView "Given by This" button (project is giver) |
|||
this.giverEntityType = "project"; |
|||
// Only override recipient if not already set by recipientEntityTypeOverride |
|||
if (!this.recipientEntityTypeOverride) { |
|||
this.recipientEntityType = "person"; |
|||
} |
|||
} else if (this.toProjectId) { |
|||
// ProjectViewView "Given to This" button (project is recipient) |
|||
this.giverEntityType = "person"; |
|||
// Only override recipient if not already set by recipientEntityTypeOverride |
|||
if (!this.recipientEntityTypeOverride) { |
|||
this.recipientEntityType = "project"; |
|||
} |
|||
} else { |
|||
// HomeView "Person" button |
|||
this.giverEntityType = "person"; |
|||
// Only override recipient if not already set by recipientEntityTypeOverride |
|||
if (!this.recipientEntityTypeOverride) { |
|||
this.recipientEntityType = "person"; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Issues with the Original Approach |
|||
|
|||
1. **Complex Logic**: Nested conditionals that were hard to follow |
|||
2. **Tight Coupling**: Views needed to understand internal logic to set the right props |
|||
3. **Inflexible**: Adding new entity type combinations required modifying the method |
|||
4. **Unclear Intent**: The relationship between props and entity types was not obvious |
|||
|
|||
## Solution |
|||
|
|||
### 1. Explicit Props |
|||
|
|||
Replace the complex logic with explicit props: |
|||
|
|||
```typescript |
|||
@Prop({ default: "person" }) giverEntityType = "person" as "person" | "project"; |
|||
@Prop({ default: "person" }) recipientEntityType = "person" as "person" | "project"; |
|||
``` |
|||
|
|||
### 2. Simple Inline Logic |
|||
|
|||
Views now use simple inline logic to determine entity types: |
|||
|
|||
```vue |
|||
<!-- HomeView --> |
|||
<GiftedDialog |
|||
ref="giftedDialog" |
|||
:giver-entity-type="showProjectsDialog ? 'project' : 'person'" |
|||
:recipient-entity-type="'person'" |
|||
/> |
|||
|
|||
<!-- ProjectViewView --> |
|||
<GiftedDialog |
|||
ref="giveDialogToThis" |
|||
:giver-entity-type="'person'" |
|||
:recipient-entity-type="'project'" |
|||
:to-project-id="projectId" |
|||
:is-from-project-view="true" |
|||
/> |
|||
|
|||
<!-- ClaimView --> |
|||
<GiftedDialog |
|||
ref="customGiveDialog" |
|||
:giver-entity-type="'person'" |
|||
:recipient-entity-type="projectInfo ? 'project' : 'person'" |
|||
:to-project-id="..." |
|||
/> |
|||
``` |
|||
|
|||
## Benefits |
|||
|
|||
### 1. **Declarative** |
|||
- Entity types are explicitly declared in the template |
|||
- No hidden logic in watchers or complex methods |
|||
- Clear intent at the call site |
|||
|
|||
### 2. **Reusable** |
|||
- Views can easily specify any combination of entity types |
|||
- No need to understand internal logic |
|||
- Simple inline logic is easy to understand |
|||
|
|||
### 3. **Maintainable** |
|||
- Adding new entity type combinations is straightforward |
|||
- Logic is visible directly in the template |
|||
- No additional files to maintain |
|||
|
|||
### 4. **Testable** |
|||
- Entity type logic is visible and predictable |
|||
- No complex state management to test |
|||
- Template logic can be easily verified |
|||
|
|||
### 5. **Type Safe** |
|||
- TypeScript ensures correct entity type values |
|||
- Compile-time validation of entity type combinations |
|||
|
|||
## Migration Guide |
|||
|
|||
### For Views Using GiftedDialog |
|||
|
|||
Simply update the template to use explicit entity type props: |
|||
|
|||
```vue |
|||
<!-- Before --> |
|||
<GiftedDialog :show-projects="showProjects" /> |
|||
|
|||
<!-- After --> |
|||
<GiftedDialog |
|||
:giver-entity-type="showProjects ? 'project' : 'person'" |
|||
:recipient-entity-type="'person'" |
|||
/> |
|||
``` |
|||
|
|||
### Common Patterns |
|||
|
|||
1. **Person-to-Person**: `giver-entity-type="'person'" recipient-entity-type="'person'"` |
|||
2. **Project-to-Person**: `giver-entity-type="'project'" recipient-entity-type="'person'"` |
|||
3. **Person-to-Project**: `giver-entity-type="'person'" recipient-entity-type="'project'"` |
|||
4. **Conditional Project**: `recipient-entity-type="hasProject ? 'project' : 'person'"` |
|||
|
|||
## Files Changed |
|||
|
|||
### Core Changes |
|||
- `src/components/GiftedDialog.vue` - Removed `updateEntityTypes()` method, added explicit props |
|||
|
|||
### View Updates |
|||
- `src/views/HomeView.vue` - Updated to use inline logic |
|||
- `src/views/ProjectViewView.vue` - Updated to use inline logic |
|||
- `src/views/ClaimView.vue` - Updated to use inline logic |
|||
- `src/views/ContactGiftingView.vue` - Updated to use inline logic |
|||
- `src/views/ContactsView.vue` - Updated to use inline logic |
|||
|
|||
## Backward Compatibility |
|||
|
|||
The refactoring maintains backward compatibility by: |
|||
- Keeping all existing props that are still needed (`fromProjectId`, `toProjectId`, `isFromProjectView`) |
|||
- Preserving the same component API for the `open()` method |
|||
- Maintaining the same template structure |
|||
|
|||
## Future Enhancements |
|||
|
|||
1. **Validation**: Add runtime validation for entity type combinations |
|||
2. **Documentation**: Add JSDoc comments to the component props |
|||
3. **Testing**: Add unit tests for the component with different entity type combinations |
|||
|
|||
## Conclusion |
|||
|
|||
This refactoring transforms `GiftedDialog` from a component with complex internal logic to a declarative, reusable component. The explicit entity type props make the component's behavior clear and predictable, while the simple inline logic keeps the code straightforward and maintainable. |
|||
|
|||
## Bug Fixes |
|||
|
|||
### Issue 1: Entity Type Preservation in Navigation |
|||
|
|||
**Problem**: When navigating from HomeView with `showProjects = true` to ContactGiftingView via "Show All", the entity type information was lost because `showAllQueryParams` returned an empty object for project contexts. |
|||
|
|||
**Solution**: Modified `EntitySelectionStep.vue` to always pass entity type information in the query parameters, even for project contexts. |
|||
|
|||
### Issue 2: Recipient Reset in ContactGiftingView |
|||
|
|||
**Problem**: When selecting a giver in ContactGiftingView, the recipient was always reset to "You" instead of preserving the current recipient. |
|||
|
|||
**Solution**: Updated ContactGiftingView to preserve the existing recipient from the context when selecting a giver, and enhanced the query parameter passing to include both giver and recipient information for better context preservation. |
|||
|
|||
### Issue 3: HomeView Project Button Entity Type Mismatch |
|||
|
|||
**Problem**: When navigating from HomeView Project button → change recipient → Show All → ContactGifting, the giver entity type was incorrectly set to "person" instead of "project". |
|||
|
|||
**Root Cause**: ContactGiftingView was inferring entity types from `fromProjectId` and `toProjectId` instead of using the explicitly passed `giverEntityType` and `recipientEntityType` from the query parameters. |
|||
|
|||
**Solution**: Updated ContactGiftingView to use the explicitly passed entity types from query parameters instead of inferring them from project IDs. |
|||
|
|||
### Files Modified for Bug Fixes |
|||
|
|||
- `src/components/EntitySelectionStep.vue` - Enhanced query parameter passing |
|||
- `src/views/ContactGiftingView.vue` - Improved context preservation logic and entity type handling |
File diff suppressed because it is too large
@ -1,81 +0,0 @@ |
|||
import { defineConfig, devices } from '@playwright/test'; |
|||
|
|||
/** |
|||
* Read environment variables from file. |
|||
* https://github.com/motdotla/dotenv
|
|||
*/ |
|||
// import dotenv from 'dotenv';
|
|||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|||
|
|||
/** |
|||
* See https://playwright.dev/docs/test-configuration.
|
|||
*/ |
|||
export default defineConfig({ |
|||
testDir: './test-playwright', |
|||
/* Run tests in files in parallel */ |
|||
fullyParallel: true, |
|||
/* Fail the build on CI if you accidentally left test.only in the source code. */ |
|||
forbidOnly: !!process.env.CI, |
|||
/* Retry on CI only */ |
|||
retries: process.env.CI ? 2 : 0, |
|||
/* Opt out of parallel tests on CI. */ |
|||
workers: process.env.CI ? 1 : undefined, |
|||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ |
|||
reporter: 'html', |
|||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ |
|||
use: { |
|||
/* Base URL to use in actions like `await page.goto('/')`. */ |
|||
baseURL: 'https://test.timesafari.app', |
|||
|
|||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ |
|||
trace: 'on-first-retry', |
|||
}, |
|||
|
|||
/* Configure projects for major browsers */ |
|||
projects: [ |
|||
{ |
|||
name: 'chromium', |
|||
use: { |
|||
...devices['Desktop Chrome'], |
|||
permissions: ["clipboard-read"], |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'firefox', |
|||
use: { ...devices['Desktop Firefox'] }, |
|||
}, |
|||
|
|||
// {
|
|||
// name: 'webkit',
|
|||
// use: { ...devices['Desktop Safari'] },
|
|||
// },
|
|||
|
|||
/* Test against mobile viewports. */ |
|||
// {
|
|||
// name: 'Mobile Chrome',
|
|||
// use: { ...devices['Pixel 5'] },
|
|||
// },
|
|||
// {
|
|||
// name: 'Mobile Safari',
|
|||
// use: { ...devices['iPhone 12'] },
|
|||
// },
|
|||
|
|||
/* Test against branded browsers. */ |
|||
// {
|
|||
// name: 'Microsoft Edge',
|
|||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
|||
// },
|
|||
// {
|
|||
// name: 'Google Chrome',
|
|||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
|||
// },
|
|||
], |
|||
|
|||
/* Run your local dev server before starting the tests */ |
|||
// webServer: {
|
|||
// command:
|
|||
// "VITE_PASSKEYS_ENABLED=true VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 npm run dev",
|
|||
// url: "http://localhost:8080",
|
|||
// reuseExistingServer: !process.env.CI,
|
|||
// },
|
|||
}); |
@ -0,0 +1,13 @@ |
|||
declare global { |
|||
interface Window { |
|||
electronAPI?: { |
|||
exportData: (fileName: string, content: string) => Promise<{ |
|||
success: boolean; |
|||
path?: string; |
|||
error?: string; |
|||
}>; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
export {}; |
@ -1,83 +0,0 @@ |
|||
import type { QueryExecResult, SqlValue } from "./database"; |
|||
|
|||
declare module '@jlongster/sql.js' { |
|||
interface SQL { |
|||
Database: new (path: string, options?: { filename: boolean }) => Database; |
|||
FS: { |
|||
mkdir: (path: string) => void; |
|||
mount: (fs: any, options: any, path: string) => void; |
|||
open: (path: string, flags: string) => any; |
|||
close: (stream: any) => void; |
|||
}; |
|||
register_for_idb: (fs: any) => void; |
|||
} |
|||
|
|||
interface Database { |
|||
exec: (sql: string, params?: unknown[]) => Promise<QueryExecResult[]>; |
|||
run: (sql: string, params?: unknown[]) => Promise<{ changes: number; lastId?: number }>; |
|||
get: (sql: string, params?: unknown[]) => Promise<SqlValue[]>; |
|||
all: (sql: string, params?: unknown[]) => Promise<SqlValue[][]>; |
|||
prepare: (sql: string) => Promise<Statement>; |
|||
close: () => void; |
|||
} |
|||
|
|||
interface Statement { |
|||
run: (params?: unknown[]) => Promise<{ changes: number; lastId?: number }>; |
|||
get: (params?: unknown[]) => Promise<SqlValue[]>; |
|||
all: (params?: unknown[]) => Promise<SqlValue[][]>; |
|||
finalize: () => void; |
|||
} |
|||
|
|||
const initSqlJs: (options?: { |
|||
locateFile?: (file: string) => string; |
|||
}) => Promise<SQL>; |
|||
|
|||
export default initSqlJs; |
|||
} |
|||
|
|||
/** |
|||
* Electron API types for the main world context bridge. |
|||
* |
|||
* These types define the secure IPC APIs exposed by the preload script |
|||
* to the renderer process for native Electron functionality. |
|||
*/ |
|||
interface ElectronAPI { |
|||
/** |
|||
* Export data to the user's Downloads folder. |
|||
* |
|||
* @param fileName - The name of the file to save (e.g., 'backup-2025-07-06.json') |
|||
* @param data - The content to write to the file (string) |
|||
* @returns Promise with success status, file path, or error message |
|||
*/ |
|||
exportData: (fileName: string, data: string) => Promise<{ |
|||
success: boolean; |
|||
path?: string; |
|||
error?: string; |
|||
}>; |
|||
} |
|||
|
|||
/** |
|||
* Global window interface extension for Electron APIs. |
|||
* |
|||
* This makes the electronAPI available on the window object |
|||
* in TypeScript without type errors. |
|||
*/ |
|||
declare global { |
|||
interface Window { |
|||
electronAPI: ElectronAPI; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Vue instance interface extension for global properties. |
|||
* |
|||
* This makes global properties available on Vue instances |
|||
* in TypeScript without type errors. |
|||
*/ |
|||
declare module 'vue' { |
|||
interface ComponentCustomProperties { |
|||
$notify: (notification: any, timeout?: number) => void; |
|||
$route: import('vue-router').RouteLocationNormalizedLoaded; |
|||
$router: import('vue-router').Router; |
|||
} |
|||
} |
@ -1,67 +0,0 @@ |
|||
import type { QueryExecResult, SqlValue } from "./database"; |
|||
|
|||
declare module '@jlongster/sql.js' { |
|||
interface SQL { |
|||
Database: new (path: string, options?: { filename: boolean }) => Database; |
|||
FS: { |
|||
mkdir: (path: string) => void; |
|||
mount: (fs: any, options: any, path: string) => void; |
|||
open: (path: string, flags: string) => any; |
|||
close: (stream: any) => void; |
|||
}; |
|||
register_for_idb: (fs: any) => void; |
|||
} |
|||
|
|||
interface Database { |
|||
exec: (sql: string, params?: unknown[]) => Promise<QueryExecResult[]>; |
|||
run: (sql: string, params?: unknown[]) => Promise<{ changes: number; lastId?: number }>; |
|||
get: (sql: string, params?: unknown[]) => Promise<SqlValue[]>; |
|||
all: (sql: string, params?: unknown[]) => Promise<SqlValue[][]>; |
|||
prepare: (sql: string) => Promise<Statement>; |
|||
close: () => void; |
|||
} |
|||
|
|||
interface Statement { |
|||
run: (params?: unknown[]) => Promise<{ changes: number; lastId?: number }>; |
|||
get: (params?: unknown[]) => Promise<SqlValue[]>; |
|||
all: (params?: unknown[]) => Promise<SqlValue[][]>; |
|||
finalize: () => void; |
|||
} |
|||
|
|||
const initSqlJs: (options?: { |
|||
locateFile?: (file: string) => string; |
|||
}) => Promise<SQL>; |
|||
|
|||
export default initSqlJs; |
|||
} |
|||
|
|||
declare module 'absurd-sql' { |
|||
import type { SQL } from '@jlongster/sql.js'; |
|||
export class SQLiteFS { |
|||
constructor(fs: any, backend: any); |
|||
} |
|||
} |
|||
|
|||
declare module 'absurd-sql/dist/indexeddb-backend' { |
|||
export default class IndexedDBBackend { |
|||
constructor(); |
|||
} |
|||
} |
|||
|
|||
declare module 'absurd-sql/dist/indexeddb-main-thread' { |
|||
import type { QueryExecResult } from './database'; |
|||
export interface SQLiteOptions { |
|||
filename?: string; |
|||
autoLoad?: boolean; |
|||
debug?: boolean; |
|||
} |
|||
|
|||
export interface SQLiteDatabase { |
|||
exec: (sql: string, params?: unknown[]) => Promise<QueryExecResult[]>; |
|||
close: () => Promise<void>; |
|||
} |
|||
|
|||
export function initSqlJs(options?: any): Promise<any>; |
|||
export function createDatabase(options?: SQLiteOptions): Promise<SQLiteDatabase>; |
|||
export function openDatabase(options?: SQLiteOptions): Promise<SQLiteDatabase>; |
|||
} |
Loading…
Reference in new issue