Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981920dd7a | ||
|
|
d189c39062 | ||
|
|
8edddb1a57 | ||
|
|
9eb07b3258 | ||
|
|
e5dffc30ff | ||
|
|
0b4e885edd | ||
|
|
b6d9b29720 | ||
|
|
b5348e42a7 | ||
|
|
a4fb3eea2d | ||
|
|
5d12c76693 | ||
|
|
d426f9c4ac | ||
|
|
340a574325 | ||
|
|
98b3a35e3c | ||
|
|
409de21fc4 | ||
|
|
17c9d32f49 | ||
|
|
25e4db395a | ||
|
|
b6ee30892f | ||
|
|
b01a450733 | ||
|
|
596f3355bf | ||
|
|
e1f9a6fa08 | ||
|
|
340e718199 | ||
|
|
5d97c98ae8 | ||
|
|
ec74fff892 | ||
|
|
1e88c0e26f | ||
|
|
3ec2364394 | ||
|
|
8b215c909d | ||
|
|
91a1c05473 | ||
|
|
66929d9b14 | ||
|
|
1e63ddcb6e | ||
|
|
51f5755f5c | ||
|
|
e5a3d622b6 | ||
|
|
a6edcd6269 | ||
|
|
b7b6be5831 | ||
|
|
cbaca0304d | ||
|
|
59d711bd90 | ||
|
|
c355de6e33 | ||
|
|
28c114a2c7 | ||
|
|
dabfe33fbe | ||
|
|
d8f2587d1c | ||
|
|
3946a8a27a | ||
|
|
4c40b80718 | ||
|
|
74989c2b64 | ||
|
|
7e17b41444 | ||
|
|
83acb028c7 | ||
|
|
786f07e067 | ||
|
|
710cc1683c | ||
|
|
ebef5d6c8d | ||
|
|
43ea7ee610 | ||
|
|
57191df416 | ||
|
|
644593a5f4 | ||
|
|
900c2521c7 | ||
|
|
182cff2b16 | ||
|
|
3b4ef908f3 | ||
|
|
a5a9e15ece | ||
|
|
a6d8f0eb8a | ||
|
|
3997a88b44 | ||
|
|
5eeeae32c6 | ||
|
|
d9895086e6 | ||
|
|
fb8d1cb8b2 | ||
|
|
70c0edbed0 | ||
|
|
55cc08d675 | ||
|
|
688a5be76e | ||
|
|
014341f320 | ||
|
|
1d5e062c76 | ||
|
|
2c5c15108a | ||
|
|
26df0fb671 |
@@ -1,310 +0,0 @@
|
||||
# Cursor Markdown Ruleset for TimeSafari Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This ruleset enforces consistent markdown formatting standards across all project
|
||||
documentation, ensuring readability, maintainability, and compliance with
|
||||
markdownlint best practices.
|
||||
|
||||
## General Formatting Standards
|
||||
|
||||
### Line Length
|
||||
|
||||
- **Maximum line length**: 80 characters
|
||||
- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line length
|
||||
enforcement
|
||||
- **Rationale**: Ensures readability across different screen sizes and terminal
|
||||
widths
|
||||
|
||||
### Blank Lines
|
||||
|
||||
- **Headings**: Must be surrounded by blank lines above and below
|
||||
- **Lists**: Must be surrounded by blank lines above and below
|
||||
- **Code blocks**: Must be surrounded by blank lines above and below
|
||||
- **Maximum consecutive blank lines**: 1 (no multiple blank lines)
|
||||
- **File start**: No blank lines at the beginning of the file
|
||||
- **File end**: Single newline character at the end
|
||||
|
||||
### Whitespace
|
||||
|
||||
- **No trailing spaces**: Remove all trailing whitespace from lines
|
||||
- **No tabs**: Use spaces for indentation
|
||||
- **Consistent indentation**: 2 spaces for list items and nested content
|
||||
|
||||
## Heading Standards
|
||||
|
||||
### Format
|
||||
|
||||
- **Style**: ATX-style headings (`#`, `##`, `###`, etc.)
|
||||
- **Case**: Title case for general headings
|
||||
- **Code references**: Use backticks for file names and technical terms
|
||||
- ✅ `### Current package.json Scripts`
|
||||
- ❌ `### Current Package.json Scripts`
|
||||
|
||||
### Hierarchy
|
||||
|
||||
- **H1 (#)**: Document title only
|
||||
- **H2 (##)**: Major sections
|
||||
- **H3 (###)**: Subsections
|
||||
- **H4 (####)**: Sub-subsections
|
||||
- **H5+**: Avoid deeper nesting
|
||||
|
||||
## List Standards
|
||||
|
||||
### Unordered Lists
|
||||
|
||||
- **Marker**: Use `-` (hyphen) consistently
|
||||
- **Indentation**: 2 spaces for nested items
|
||||
- **Blank lines**: Surround lists with blank lines
|
||||
|
||||
### Ordered Lists
|
||||
|
||||
- **Format**: `1.`, `2.`, `3.` (sequential numbering)
|
||||
- **Indentation**: 2 spaces for nested items
|
||||
- **Blank lines**: Surround lists with blank lines
|
||||
|
||||
### Task Lists
|
||||
|
||||
- **Format**: `- [ ]` for incomplete, `- [x]` for complete
|
||||
- **Use case**: Project planning, checklists, implementation tracking
|
||||
|
||||
## Code Block Standards
|
||||
|
||||
### Fenced Code Blocks
|
||||
|
||||
- **Syntax**: Triple backticks with language specification
|
||||
- **Languages**: `json`, `bash`, `typescript`, `javascript`, `yaml`, `markdown`
|
||||
- **Blank lines**: Must be surrounded by blank lines above and below
|
||||
- **Line length**: No enforcement within code blocks
|
||||
|
||||
### Inline Code
|
||||
|
||||
- **Format**: Single backticks for inline code references
|
||||
- **Use case**: File names, commands, variables, properties
|
||||
|
||||
## Special Content Standards
|
||||
|
||||
### JSON Examples
|
||||
|
||||
```json
|
||||
{
|
||||
"property": "value",
|
||||
"nested": {
|
||||
"property": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shell Commands
|
||||
|
||||
```bash
|
||||
# Command with comment
|
||||
npm run build:web
|
||||
|
||||
# Multi-line command
|
||||
VITE_GIT_HASH=`git log -1 --pretty=format:%h` \
|
||||
vite build --config vite.config.web.mts
|
||||
```
|
||||
|
||||
### TypeScript Examples
|
||||
|
||||
```typescript
|
||||
// Function with JSDoc
|
||||
/**
|
||||
* Get environment configuration
|
||||
* @param env - Environment name
|
||||
* @returns Environment config object
|
||||
*/
|
||||
const getEnvironmentConfig = (env: string) => {
|
||||
switch (env) {
|
||||
case 'prod':
|
||||
return { /* production settings */ };
|
||||
default:
|
||||
return { /* development settings */ };
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## File Structure Standards
|
||||
|
||||
### Document Header
|
||||
|
||||
```markdown
|
||||
# Document Title
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: YYYY-MM-DD
|
||||
**Status**: 🎯 **STATUS** - Brief description
|
||||
|
||||
## Overview
|
||||
|
||||
Brief description of the document's purpose and scope.
|
||||
```
|
||||
|
||||
### Section Organization
|
||||
|
||||
1. **Overview/Introduction**
|
||||
2. **Current State Analysis**
|
||||
3. **Implementation Plan**
|
||||
4. **Technical Details**
|
||||
5. **Testing & Validation**
|
||||
6. **Next Steps**
|
||||
|
||||
## Markdownlint Configuration
|
||||
|
||||
### Required Rules
|
||||
|
||||
```json
|
||||
{
|
||||
"MD013": { "code_blocks": false },
|
||||
"MD012": true,
|
||||
"MD022": true,
|
||||
"MD031": true,
|
||||
"MD032": true,
|
||||
"MD047": true,
|
||||
"MD009": true
|
||||
}
|
||||
```
|
||||
|
||||
### Rule Explanations
|
||||
|
||||
- **MD013**: Line length (disabled for code blocks)
|
||||
- **MD012**: No multiple consecutive blank lines
|
||||
- **MD022**: Headings should be surrounded by blank lines
|
||||
- **MD031**: Fenced code blocks should be surrounded by blank lines
|
||||
- **MD032**: Lists should be surrounded by blank lines
|
||||
- **MD047**: Files should end with a single newline
|
||||
- **MD009**: No trailing spaces
|
||||
|
||||
## Validation Commands
|
||||
|
||||
### Check Single File
|
||||
|
||||
```bash
|
||||
npx markdownlint docs/filename.md
|
||||
```
|
||||
|
||||
### Check All Documentation
|
||||
|
||||
```bash
|
||||
npx markdownlint docs/
|
||||
```
|
||||
|
||||
### Auto-fix Common Issues
|
||||
|
||||
```bash
|
||||
# Remove trailing spaces
|
||||
sed -i 's/[[:space:]]*$//' docs/filename.md
|
||||
|
||||
# Remove multiple blank lines
|
||||
sed -i '/^$/N;/^\n$/D' docs/filename.md
|
||||
|
||||
# Add newline at end if missing
|
||||
echo "" >> docs/filename.md
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Implementation Plans
|
||||
|
||||
```markdown
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Foundation (Day 1)
|
||||
|
||||
#### 1.1 Component Setup
|
||||
|
||||
- [ ] Create new component file
|
||||
- [ ] Add basic structure
|
||||
- [ ] Implement core functionality
|
||||
|
||||
#### 1.2 Configuration
|
||||
|
||||
- [ ] Update configuration files
|
||||
- [ ] Add environment variables
|
||||
- [ ] Test configuration loading
|
||||
```
|
||||
|
||||
### Status Tracking
|
||||
|
||||
```markdown
|
||||
**Status**: ✅ **COMPLETE** - All phases finished
|
||||
**Progress**: 75% (15/20 components)
|
||||
**Next**: Ready for testing phase
|
||||
```
|
||||
|
||||
### Performance Metrics
|
||||
|
||||
```markdown
|
||||
#### 📊 Performance Metrics
|
||||
- **Build Time**: 2.3 seconds (50% faster than baseline)
|
||||
- **Bundle Size**: 1.2MB (30% reduction)
|
||||
- **Success Rate**: 100% (no failures in 50 builds)
|
||||
```
|
||||
|
||||
## Enforcement
|
||||
|
||||
### Pre-commit Hooks
|
||||
|
||||
- Run markdownlint on all changed markdown files
|
||||
- Block commits with linting violations
|
||||
- Auto-fix common issues when possible
|
||||
|
||||
### CI/CD Integration
|
||||
|
||||
- Include markdownlint in build pipeline
|
||||
- Generate reports for documentation quality
|
||||
- Fail builds with critical violations
|
||||
|
||||
### Team Guidelines
|
||||
|
||||
- All documentation PRs must pass markdownlint
|
||||
- Use provided templates for new documents
|
||||
- Follow established patterns for consistency
|
||||
|
||||
## Templates
|
||||
|
||||
### New Document Template
|
||||
|
||||
```markdown
|
||||
# Document Title
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: YYYY-MM-DD
|
||||
**Status**: 🎯 **PLANNING** - Ready for Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
Brief description of the document's purpose and scope.
|
||||
|
||||
## Current State
|
||||
|
||||
Description of current situation or problem.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Foundation
|
||||
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review and approve plan**
|
||||
2. **Begin implementation**
|
||||
3. **Test and validate**
|
||||
|
||||
---
|
||||
|
||||
**Status**: Ready for implementation
|
||||
**Priority**: Medium
|
||||
**Estimated Effort**: X days
|
||||
**Dependencies**: None
|
||||
**Stakeholders**: Development team
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-07-09
|
||||
**Version**: 1.0
|
||||
**Maintainer**: Matthew Raymer
|
||||
@@ -7,13 +7,13 @@ alwaysApply: true
|
||||
|
||||
## 1. Platform Support Matrix
|
||||
|
||||
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) |
|
||||
|---------|-----------|-------------------|-------------------|
|
||||
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented |
|
||||
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented |
|
||||
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs |
|
||||
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented |
|
||||
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks |
|
||||
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | PyWebView (Desktop) |
|
||||
|---------|-----------|-------------------|-------------------|-------------------|
|
||||
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | Not Implemented |
|
||||
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | Not Implemented |
|
||||
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | PyWebView Python Bridge |
|
||||
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | Not Implemented |
|
||||
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | process.env checks |
|
||||
|
||||
## 2. Project Structure
|
||||
|
||||
@@ -42,6 +42,7 @@ src/
|
||||
├── main.common.ts # Shared initialization
|
||||
├── main.capacitor.ts # Mobile entry
|
||||
├── main.electron.ts # Electron entry
|
||||
├── main.pywebview.ts # PyWebView entry
|
||||
└── main.web.ts # Web/PWA entry
|
||||
```
|
||||
|
||||
@@ -51,7 +52,9 @@ root/
|
||||
├── vite.config.common.mts # Shared config
|
||||
├── vite.config.capacitor.mts # Mobile build
|
||||
├── vite.config.electron.mts # Electron build
|
||||
└── vite.config.web.mts # Web/PWA build
|
||||
├── vite.config.pywebview.mts # PyWebView build
|
||||
├── vite.config.web.mts # Web/PWA build
|
||||
└── vite.config.utils.mts # Build utilities
|
||||
```
|
||||
|
||||
## 3. Service Architecture
|
||||
@@ -65,7 +68,8 @@ services/
|
||||
├── platforms/ # Platform-specific services
|
||||
│ ├── WebPlatformService.ts
|
||||
│ ├── CapacitorPlatformService.ts
|
||||
│ └── ElectronPlatformService.ts
|
||||
│ ├── ElectronPlatformService.ts
|
||||
│ └── PyWebViewPlatformService.ts
|
||||
└── factory/ # Service factories
|
||||
└── PlatformServiceFactory.ts
|
||||
```
|
||||
@@ -149,7 +153,7 @@ export function createBuildConfig(mode: string) {
|
||||
return {
|
||||
define: {
|
||||
'process.env.VITE_PLATFORM': JSON.stringify(mode),
|
||||
// PWA is automatically enabled for web platforms via build configuration
|
||||
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative),
|
||||
__IS_MOBILE__: JSON.stringify(isCapacitor),
|
||||
__USE_QR_READER__: JSON.stringify(!isCapacitor)
|
||||
}
|
||||
@@ -163,7 +167,8 @@ export function createBuildConfig(mode: string) {
|
||||
# Build commands from package.json
|
||||
"build:web": "vite build --config vite.config.web.mts",
|
||||
"build:capacitor": "vite build --config vite.config.capacitor.mts",
|
||||
"build:electron": "vite build --config vite.config.electron.mts"
|
||||
"build:electron": "vite build --config vite.config.electron.mts",
|
||||
"build:pywebview": "vite build --config vite.config.pywebview.mts"
|
||||
```
|
||||
|
||||
## 6. Testing Strategy
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
description: rules used while developing
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
✅ use system date command to timestamp all interactions with accurate date and time
|
||||
✅ python script files must always have a blank line at their end
|
||||
✅ remove whitespace at the end of lines
|
||||
✅ use npm run lint-fix to check for warnings
|
||||
✅ do not use npm run dev let me handle running and supplying feedback
|
||||
✅ do not add or commit for the user; let him control that process
|
||||
|
||||
always preview changes and commit message to use and allow me to copy and paste
|
||||
✅ Preferred Commit Message Format
|
||||
|
||||
Short summary in the first line (concise and high-level).
|
||||
Avoid long commit bodies unless truly necessary.
|
||||
|
||||
✅ Valued Content in Commit Messages
|
||||
|
||||
Specific fixes or features.
|
||||
Symptoms or problems that were fixed.
|
||||
Notes about tests passing or TS/linting errors being resolved (briefly).
|
||||
|
||||
❌ Avoid in Commit Messages
|
||||
|
||||
Vague terms: “improved”, “enhanced”, “better” — especially from AI.
|
||||
Minor changes: small doc tweaks, one-liners, cleanup, or lint fixes.
|
||||
Redundant blurbs: repeated across files or too generic.
|
||||
Multiple overlapping purposes in a single commit — prefer narrow, focused commits.
|
||||
Long explanations of what can be deduced from good in-line code comments.
|
||||
|
||||
Guiding Principle
|
||||
|
||||
Let code and inline documentation speak for themselves. Use commits to highlight what isn't obvious from reading the code.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
All references in the codebase to Dexie apply only to migration from IndexedDb to Sqlite and will be deprecated in future versions.
|
||||
@@ -1,314 +0,0 @@
|
||||
---
|
||||
globs: *.md
|
||||
alwaysApply: false
|
||||
---
|
||||
# Cursor Markdown Ruleset for TimeSafari Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This ruleset enforces consistent markdown formatting standards across all project
|
||||
documentation, ensuring readability, maintainability, and compliance with
|
||||
markdownlint best practices.
|
||||
|
||||
## General Formatting Standards
|
||||
|
||||
### Line Length
|
||||
|
||||
- **Maximum line length**: 80 characters
|
||||
- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line length
|
||||
enforcement
|
||||
- **Rationale**: Ensures readability across different screen sizes and terminal
|
||||
widths
|
||||
|
||||
### Blank Lines
|
||||
|
||||
- **Headings**: Must be surrounded by blank lines above and below
|
||||
- **Lists**: Must be surrounded by blank lines above and below
|
||||
- **Code blocks**: Must be surrounded by blank lines above and below
|
||||
- **Maximum consecutive blank lines**: 1 (no multiple blank lines)
|
||||
- **File start**: No blank lines at the beginning of the file
|
||||
- **File end**: Single newline character at the end
|
||||
|
||||
### Whitespace
|
||||
|
||||
- **No trailing spaces**: Remove all trailing whitespace from lines
|
||||
- **No tabs**: Use spaces for indentation
|
||||
- **Consistent indentation**: 2 spaces for list items and nested content
|
||||
|
||||
## Heading Standards
|
||||
|
||||
### Format
|
||||
|
||||
- **Style**: ATX-style headings (`#`, `##`, `###`, etc.)
|
||||
- **Case**: Title case for general headings
|
||||
- **Code references**: Use backticks for file names and technical terms
|
||||
- ✅ `### Current package.json Scripts`
|
||||
- ❌ `### Current Package.json Scripts`
|
||||
|
||||
### Hierarchy
|
||||
|
||||
- **H1 (#)**: Document title only
|
||||
- **H2 (##)**: Major sections
|
||||
- **H3 (###)**: Subsections
|
||||
- **H4 (####)**: Sub-subsections
|
||||
- **H5+**: Avoid deeper nesting
|
||||
|
||||
## List Standards
|
||||
|
||||
### Unordered Lists
|
||||
|
||||
- **Marker**: Use `-` (hyphen) consistently
|
||||
- **Indentation**: 2 spaces for nested items
|
||||
- **Blank lines**: Surround lists with blank lines
|
||||
|
||||
### Ordered Lists
|
||||
|
||||
- **Format**: `1.`, `2.`, `3.` (sequential numbering)
|
||||
- **Indentation**: 2 spaces for nested items
|
||||
- **Blank lines**: Surround lists with blank lines
|
||||
|
||||
### Task Lists
|
||||
|
||||
- **Format**: `- [ ]` for incomplete, `- [x]` for complete
|
||||
- **Use case**: Project planning, checklists, implementation tracking
|
||||
|
||||
## Code Block Standards
|
||||
|
||||
### Fenced Code Blocks
|
||||
|
||||
- **Syntax**: Triple backticks with language specification
|
||||
- **Languages**: `json`, `bash`, `typescript`, `javascript`, `yaml`, `markdown`
|
||||
- **Blank lines**: Must be surrounded by blank lines above and below
|
||||
- **Line length**: No enforcement within code blocks
|
||||
|
||||
### Inline Code
|
||||
|
||||
- **Format**: Single backticks for inline code references
|
||||
- **Use case**: File names, commands, variables, properties
|
||||
|
||||
## Special Content Standards
|
||||
|
||||
### JSON Examples
|
||||
|
||||
```json
|
||||
{
|
||||
"property": "value",
|
||||
"nested": {
|
||||
"property": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shell Commands
|
||||
|
||||
```bash
|
||||
# Command with comment
|
||||
npm run build:web
|
||||
|
||||
# Multi-line command
|
||||
VITE_GIT_HASH=`git log -1 --pretty=format:%h` \
|
||||
vite build --config vite.config.web.mts
|
||||
```
|
||||
|
||||
### TypeScript Examples
|
||||
|
||||
```typescript
|
||||
// Function with JSDoc
|
||||
/**
|
||||
* Get environment configuration
|
||||
* @param env - Environment name
|
||||
* @returns Environment config object
|
||||
*/
|
||||
const getEnvironmentConfig = (env: string) => {
|
||||
switch (env) {
|
||||
case 'prod':
|
||||
return { /* production settings */ };
|
||||
default:
|
||||
return { /* development settings */ };
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## File Structure Standards
|
||||
|
||||
### Document Header
|
||||
|
||||
```markdown
|
||||
# Document Title
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: YYYY-MM-DD
|
||||
**Status**: 🎯 **STATUS** - Brief description
|
||||
|
||||
## Overview
|
||||
|
||||
Brief description of the document's purpose and scope.
|
||||
```
|
||||
|
||||
### Section Organization
|
||||
|
||||
1. **Overview/Introduction**
|
||||
2. **Current State Analysis**
|
||||
3. **Implementation Plan**
|
||||
4. **Technical Details**
|
||||
5. **Testing & Validation**
|
||||
6. **Next Steps**
|
||||
|
||||
## Markdownlint Configuration
|
||||
|
||||
### Required Rules
|
||||
|
||||
```json
|
||||
{
|
||||
"MD013": { "code_blocks": false },
|
||||
"MD012": true,
|
||||
"MD022": true,
|
||||
"MD031": true,
|
||||
"MD032": true,
|
||||
"MD047": true,
|
||||
"MD009": true
|
||||
}
|
||||
```
|
||||
|
||||
### Rule Explanations
|
||||
|
||||
- **MD013**: Line length (disabled for code blocks)
|
||||
- **MD012**: No multiple consecutive blank lines
|
||||
- **MD022**: Headings should be surrounded by blank lines
|
||||
- **MD031**: Fenced code blocks should be surrounded by blank lines
|
||||
- **MD032**: Lists should be surrounded by blank lines
|
||||
- **MD047**: Files should end with a single newline
|
||||
- **MD009**: No trailing spaces
|
||||
|
||||
## Validation Commands
|
||||
|
||||
### Check Single File
|
||||
|
||||
```bash
|
||||
npx markdownlint docs/filename.md
|
||||
```
|
||||
|
||||
### Check All Documentation
|
||||
|
||||
```bash
|
||||
npx markdownlint docs/
|
||||
```
|
||||
|
||||
### Auto-fix Common Issues
|
||||
|
||||
```bash
|
||||
# Remove trailing spaces
|
||||
sed -i 's/[[:space:]]*$//' docs/filename.md
|
||||
|
||||
# Remove multiple blank lines
|
||||
sed -i '/^$/N;/^\n$/D' docs/filename.md
|
||||
|
||||
# Add newline at end if missing
|
||||
echo "" >> docs/filename.md
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Implementation Plans
|
||||
|
||||
```markdown
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Foundation (Day 1)
|
||||
|
||||
#### 1.1 Component Setup
|
||||
|
||||
- [ ] Create new component file
|
||||
- [ ] Add basic structure
|
||||
- [ ] Implement core functionality
|
||||
|
||||
#### 1.2 Configuration
|
||||
|
||||
- [ ] Update configuration files
|
||||
- [ ] Add environment variables
|
||||
- [ ] Test configuration loading
|
||||
```
|
||||
|
||||
### Status Tracking
|
||||
|
||||
```markdown
|
||||
**Status**: ✅ **COMPLETE** - All phases finished
|
||||
**Progress**: 75% (15/20 components)
|
||||
**Next**: Ready for testing phase
|
||||
```
|
||||
|
||||
### Performance Metrics
|
||||
|
||||
```markdown
|
||||
#### 📊 Performance Metrics
|
||||
- **Build Time**: 2.3 seconds (50% faster than baseline)
|
||||
- **Bundle Size**: 1.2MB (30% reduction)
|
||||
- **Success Rate**: 100% (no failures in 50 builds)
|
||||
```
|
||||
|
||||
## Enforcement
|
||||
|
||||
### Pre-commit Hooks
|
||||
|
||||
- Run markdownlint on all changed markdown files
|
||||
- Block commits with linting violations
|
||||
- Auto-fix common issues when possible
|
||||
|
||||
### CI/CD Integration
|
||||
|
||||
- Include markdownlint in build pipeline
|
||||
- Generate reports for documentation quality
|
||||
- Fail builds with critical violations
|
||||
|
||||
### Team Guidelines
|
||||
|
||||
- All documentation PRs must pass markdownlint
|
||||
- Use provided templates for new documents
|
||||
- Follow established patterns for consistency
|
||||
|
||||
## Templates
|
||||
|
||||
### New Document Template
|
||||
|
||||
```markdown
|
||||
# Document Title
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: YYYY-MM-DD
|
||||
**Status**: 🎯 **PLANNING** - Ready for Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
Brief description of the document's purpose and scope.
|
||||
|
||||
## Current State
|
||||
|
||||
Description of current situation or problem.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Foundation
|
||||
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review and approve plan**
|
||||
2. **Begin implementation**
|
||||
3. **Test and validate**
|
||||
|
||||
---
|
||||
|
||||
**Status**: Ready for implementation
|
||||
**Priority**: Medium
|
||||
**Estimated Effort**: X days
|
||||
**Dependencies**: None
|
||||
**Stakeholders**: Development team
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-07-09
|
||||
**Version**: 1.0
|
||||
**Maintainer**: Matthew Raymer
|
||||
@@ -0,0 +1,267 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# wa-sqlite Usage Guide
|
||||
|
||||
## Table of Contents
|
||||
- [1. Overview](#1-overview)
|
||||
- [2. Installation](#2-installation)
|
||||
- [3. Basic Setup](#3-basic-setup)
|
||||
- [3.1 Import and Initialize](#31-import-and-initialize)
|
||||
- [3.2 Basic Database Operations](#32-basic-database-operations)
|
||||
- [4. Virtual File Systems (VFS)](#4-virtual-file-systems-vfs)
|
||||
- [4.1 Available VFS Options](#41-available-vfs-options)
|
||||
- [4.2 Using a VFS](#42-using-a-vfs)
|
||||
- [5. Best Practices](#5-best-practices)
|
||||
- [5.1 Error Handling](#51-error-handling)
|
||||
- [5.2 Transaction Management](#52-transaction-management)
|
||||
- [5.3 Prepared Statements](#53-prepared-statements)
|
||||
- [6. Performance Considerations](#6-performance-considerations)
|
||||
- [7. Common Issues and Solutions](#7-common-issues-and-solutions)
|
||||
- [8. TypeScript Support](#8-typescript-support)
|
||||
|
||||
## 1. Overview
|
||||
wa-sqlite is a WebAssembly build of SQLite that enables SQLite database operations in web browsers and JavaScript environments. It provides both synchronous and asynchronous builds, with support for custom virtual file systems (VFS) for persistent storage.
|
||||
|
||||
## 2. Installation
|
||||
```bash
|
||||
npm install wa-sqlite
|
||||
# or
|
||||
yarn add wa-sqlite
|
||||
```
|
||||
|
||||
## 3. Basic Setup
|
||||
|
||||
### 3.1 Import and Initialize
|
||||
```javascript
|
||||
// Choose one of these imports based on your needs:
|
||||
// - wa-sqlite.mjs: Synchronous build
|
||||
// - wa-sqlite-async.mjs: Asynchronous build (required for async VFS)
|
||||
// - wa-sqlite-jspi.mjs: JSPI-based async build (experimental, Chromium only)
|
||||
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite.mjs';
|
||||
import * as SQLite from 'wa-sqlite';
|
||||
|
||||
async function initDatabase() {
|
||||
// Initialize SQLite module
|
||||
const module = await SQLiteESMFactory();
|
||||
const sqlite3 = SQLite.Factory(module);
|
||||
|
||||
// Open database (returns a Promise)
|
||||
const db = await sqlite3.open_v2('myDatabase');
|
||||
return { sqlite3, db };
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Basic Database Operations
|
||||
```javascript
|
||||
async function basicOperations() {
|
||||
const { sqlite3, db } = await initDatabase();
|
||||
|
||||
try {
|
||||
// Create a table
|
||||
await sqlite3.exec(db, `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE
|
||||
)
|
||||
`);
|
||||
|
||||
// Insert data
|
||||
await sqlite3.exec(db, `
|
||||
INSERT INTO users (name, email)
|
||||
VALUES ('John Doe', 'john@example.com')
|
||||
`);
|
||||
|
||||
// Query data
|
||||
const results = [];
|
||||
await sqlite3.exec(db, 'SELECT * FROM users', (row, columns) => {
|
||||
results.push({ row, columns });
|
||||
});
|
||||
|
||||
return results;
|
||||
} finally {
|
||||
// Always close the database when done
|
||||
await sqlite3.close(db);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Virtual File Systems (VFS)
|
||||
|
||||
### 4.1 Available VFS Options
|
||||
wa-sqlite provides several VFS implementations for persistent storage:
|
||||
|
||||
1. **IDBBatchAtomicVFS** (Recommended for general use)
|
||||
- Uses IndexedDB with batch atomic writes
|
||||
- Works in all contexts (Window, Worker, Service Worker)
|
||||
- Supports WAL mode
|
||||
- Best performance with `PRAGMA synchronous=normal`
|
||||
|
||||
2. **IDBMirrorVFS**
|
||||
- Keeps files in memory, persists to IndexedDB
|
||||
- Works in all contexts
|
||||
- Good for smaller databases
|
||||
|
||||
3. **OPFS-based VFS** (Origin Private File System)
|
||||
- Various implementations available:
|
||||
- AccessHandlePoolVFS
|
||||
- OPFSAdaptiveVFS
|
||||
- OPFSCoopSyncVFS
|
||||
- OPFSPermutedVFS
|
||||
- Better performance but limited to Worker contexts
|
||||
|
||||
### 4.2 Using a VFS
|
||||
```javascript
|
||||
import { IDBBatchAtomicVFS } from 'wa-sqlite/src/examples/IDBBatchAtomicVFS.js';
|
||||
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs';
|
||||
import * as SQLite from 'wa-sqlite';
|
||||
|
||||
async function initDatabaseWithVFS() {
|
||||
const module = await SQLiteESMFactory();
|
||||
const sqlite3 = SQLite.Factory(module);
|
||||
|
||||
// Register VFS
|
||||
const vfs = await IDBBatchAtomicVFS.create('myApp', module);
|
||||
sqlite3.vfs_register(vfs, true);
|
||||
|
||||
// Open database with VFS
|
||||
const db = await sqlite3.open_v2('myDatabase');
|
||||
|
||||
// Configure for better performance
|
||||
await sqlite3.exec(db, 'PRAGMA synchronous = normal');
|
||||
await sqlite3.exec(db, 'PRAGMA journal_mode = WAL');
|
||||
|
||||
return { sqlite3, db };
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Best Practices
|
||||
|
||||
### 5.1 Error Handling
|
||||
```javascript
|
||||
async function safeDatabaseOperation() {
|
||||
const { sqlite3, db } = await initDatabase();
|
||||
|
||||
try {
|
||||
await sqlite3.exec(db, 'SELECT * FROM non_existent_table');
|
||||
} catch (error) {
|
||||
if (error.code === SQLite.SQLITE_ERROR) {
|
||||
console.error('SQL error:', error.message);
|
||||
} else {
|
||||
console.error('Database error:', error);
|
||||
}
|
||||
} finally {
|
||||
await sqlite3.close(db);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Transaction Management
|
||||
```javascript
|
||||
async function transactionExample() {
|
||||
const { sqlite3, db } = await initDatabase();
|
||||
|
||||
try {
|
||||
await sqlite3.exec(db, 'BEGIN TRANSACTION');
|
||||
|
||||
// Perform multiple operations
|
||||
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Alice']);
|
||||
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Bob']);
|
||||
|
||||
await sqlite3.exec(db, 'COMMIT');
|
||||
} catch (error) {
|
||||
await sqlite3.exec(db, 'ROLLBACK');
|
||||
throw error;
|
||||
} finally {
|
||||
await sqlite3.close(db);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Prepared Statements
|
||||
```javascript
|
||||
async function preparedStatementExample() {
|
||||
const { sqlite3, db } = await initDatabase();
|
||||
|
||||
try {
|
||||
// Prepare statement
|
||||
const stmt = await sqlite3.prepare(db, 'SELECT * FROM users WHERE id = ?');
|
||||
|
||||
// Execute with different parameters
|
||||
await sqlite3.bind(stmt, 1, 1);
|
||||
while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) {
|
||||
const row = sqlite3.row(stmt);
|
||||
console.log(row);
|
||||
}
|
||||
|
||||
// Reset and reuse
|
||||
await sqlite3.reset(stmt);
|
||||
await sqlite3.bind(stmt, 1, 2);
|
||||
// ... execute again
|
||||
|
||||
await sqlite3.finalize(stmt);
|
||||
} finally {
|
||||
await sqlite3.close(db);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Performance Considerations
|
||||
|
||||
1. **VFS Selection**
|
||||
- Use IDBBatchAtomicVFS for general-purpose applications
|
||||
- Consider OPFS-based VFS for better performance in Worker contexts
|
||||
- Use MemoryVFS for temporary databases
|
||||
|
||||
2. **Configuration**
|
||||
- Set appropriate page size (default is usually fine)
|
||||
- Use WAL mode for better concurrency
|
||||
- Consider `PRAGMA synchronous=normal` for better performance
|
||||
- Adjust cache size based on your needs
|
||||
|
||||
3. **Concurrency**
|
||||
- Use transactions for multiple operations
|
||||
- Be aware of VFS-specific concurrency limitations
|
||||
- Consider using Web Workers for heavy database operations
|
||||
|
||||
## 7. Common Issues and Solutions
|
||||
|
||||
1. **Database Locking**
|
||||
- Use appropriate transaction isolation levels
|
||||
- Implement retry logic for busy errors
|
||||
- Consider using WAL mode
|
||||
|
||||
2. **Storage Limitations**
|
||||
- Be aware of browser storage quotas
|
||||
- Implement cleanup strategies
|
||||
- Monitor database size
|
||||
|
||||
3. **Cross-Context Access**
|
||||
- Use appropriate VFS for your context
|
||||
- Consider message passing for cross-context communication
|
||||
- Be aware of storage access limitations
|
||||
|
||||
## 8. TypeScript Support
|
||||
wa-sqlite includes TypeScript definitions. The main types are:
|
||||
|
||||
```typescript
|
||||
type SQLiteCompatibleType = number | string | Uint8Array | Array<number> | bigint | null;
|
||||
|
||||
interface SQLiteAPI {
|
||||
open_v2(filename: string, flags?: number, zVfs?: string): Promise<number>;
|
||||
exec(db: number, sql: string, callback?: (row: any[], columns: string[]) => void): Promise<number>;
|
||||
close(db: number): Promise<number>;
|
||||
// ... other methods
|
||||
}
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Official GitHub Repository](https://github.com/rhashimoto/wa-sqlite)
|
||||
- [Online Demo](https://rhashimoto.github.io/wa-sqlite/demo/)
|
||||
- [API Reference](https://rhashimoto.github.io/wa-sqlite/docs/)
|
||||
- [FAQ](https://github.com/rhashimoto/wa-sqlite/issues?q=is%3Aissue+label%3Afaq+)
|
||||
- [Discussion Forums](https://github.com/rhashimoto/wa-sqlite/discussions)
|
||||
@@ -1,171 +0,0 @@
|
||||
# TimeSafari Docker Ignore File
|
||||
# Author: Matthew Raymer
|
||||
# Description: Excludes unnecessary files from Docker build context
|
||||
#
|
||||
# Benefits:
|
||||
# - Faster build times
|
||||
# - Smaller build context
|
||||
# - Reduced image size
|
||||
# - Better security (excludes sensitive files)
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build outputs
|
||||
# dist - Allow dist directory for Docker builds (contains pre-built assets)
|
||||
dist-*
|
||||
build
|
||||
*.tsbuildinfo
|
||||
|
||||
# Development files
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
BUILDING.md
|
||||
LICENSE
|
||||
|
||||
# IDE and editor files
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# Test files
|
||||
test-playwright
|
||||
test-playwright-results
|
||||
test-results
|
||||
test-scripts
|
||||
|
||||
# Documentation
|
||||
doc
|
||||
|
||||
# Scripts (keep only what's needed for build)
|
||||
scripts/test-*.sh
|
||||
scripts/*.js
|
||||
scripts/README.md
|
||||
|
||||
# Platform-specific files
|
||||
android
|
||||
ios
|
||||
electron
|
||||
|
||||
# Docker files (avoid recursive copying)
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
|
||||
# CI/CD files
|
||||
.github
|
||||
.gitlab-ci.yml
|
||||
.travis.yml
|
||||
.circleci
|
||||
|
||||
# Temporary files
|
||||
tmp
|
||||
temp
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.backup
|
||||
|
||||
# Archive files
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.rar
|
||||
|
||||
# Certificate files
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.p12
|
||||
|
||||
# Configuration files that might contain secrets
|
||||
*.secrets
|
||||
secrets.json
|
||||
config.local.json
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
# 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
|
||||
VITE_APP_SERVER=http://localhost:3000
|
||||
# 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
|
||||
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
|
||||
|
||||
@@ -51,81 +51,6 @@ vendor/
|
||||
# Build logs
|
||||
build_logs/
|
||||
|
||||
# PWA icon files generated by capacitor-assets
|
||||
icons
|
||||
android/app/src/main/assets/public
|
||||
android/app/src/main/res
|
||||
|
||||
*.log
|
||||
|
||||
# Generated Android assets and resources (should be generated during build)
|
||||
android/app/src/main/assets/public/
|
||||
|
||||
# Generated Android resources (icons, splash screens, etc.)
|
||||
android/app/src/main/res/drawable*/
|
||||
android/app/src/main/res/mipmap*/
|
||||
android/app/src/main/res/values/ic_launcher_background.xml
|
||||
|
||||
# Keep these Android configuration files in version control:
|
||||
# - android/app/src/main/assets/capacitor.plugins.json
|
||||
# - android/app/src/main/res/values/strings.xml
|
||||
# - android/app/src/main/res/values/styles.xml
|
||||
# - android/app/src/main/res/layout/activity_main.xml
|
||||
# - android/app/src/main/res/xml/config.xml
|
||||
# - android/app/src/main/res/xml/file_paths.xml
|
||||
|
||||
sql-wasm.wasm
|
||||
|
||||
# Temporary and generated files
|
||||
temp.*
|
||||
*.tmp
|
||||
*.temp
|
||||
*.bak
|
||||
*.cache
|
||||
git.diff.*
|
||||
*.har
|
||||
|
||||
# Development artifacts
|
||||
dev-dist/
|
||||
*.map
|
||||
|
||||
# OS generated files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Capacitor build outputs and generated files
|
||||
android/app/build/
|
||||
android/capacitor-cordova-android-plugins/build/
|
||||
ios/App/App/public/assets/
|
||||
ios/App/App/build/
|
||||
ios/App/build/
|
||||
|
||||
# Capacitor build artifacts (covered by android/app/build/ above)
|
||||
|
||||
# Keep these Capacitor files in version control:
|
||||
# - capacitor.config.json (root, electron, ios)
|
||||
# - src/main.capacitor.ts
|
||||
# - vite.config.capacitor.mts
|
||||
# - android/capacitor.settings.gradle
|
||||
# - android/app/capacitor.build.gradle
|
||||
# - android/app/src/main/assets/capacitor.plugins.json
|
||||
|
||||
# Electron build outputs and generated files
|
||||
electron/build/
|
||||
electron/app/
|
||||
electron/dist/
|
||||
electron/out/
|
||||
|
||||
# Keep these Electron files in version control:
|
||||
# - electron/src/preload.ts (source)
|
||||
# - electron/src/index.ts (source)
|
||||
# - electron/src/setup.ts (source)
|
||||
# - electron/package.json
|
||||
# - electron/electron-builder.config.json
|
||||
# - electron/build-packages.sh
|
||||
# - electron/live-runner.js
|
||||
# - electron/resources/electron-publisher-custom.js
|
||||
|
||||
# Gradle cache files
|
||||
android/.gradle/file-system.probe
|
||||
android/.gradle/caches/
|
||||
coverage
|
||||
@@ -1 +0,0 @@
|
||||
{"MD013": {"code_blocks": false}}
|
||||
@@ -5,46 +5,6 @@ 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
|
||||
### Changed
|
||||
- Photo is pinned to profile mode
|
||||
### Fixed
|
||||
- Deep link URLs (and other prod settings)
|
||||
- Error in BVC begin view
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Photo is pinned to profile mode
|
||||
|
||||
|
||||
## [1.0.2] - 2025.06.20 - 276e0a741bc327de3380c4e508cccb7fee58c06d
|
||||
### Added
|
||||
- Version on feed title
|
||||
|
||||
|
||||
## [1.0.1] - 2025.06.20
|
||||
### Added
|
||||
- Allow a user to block someone else's content from view
|
||||
|
||||
|
||||
## [1.0.0] - 2025.06.20 - 5aa693de6337e5dbb278bfddc6bd39094bc14f73
|
||||
### Added
|
||||
- Web-oriented migration from IndexedDB to SQLite
|
||||
|
||||
|
||||
## [0.5.8]
|
||||
### Added
|
||||
- /deep-link/ path for URLs that are shared with people
|
||||
### Changed
|
||||
- External links now go to /deep-link/...
|
||||
- Feed visuals now have arrow imagery from giver to receiver
|
||||
|
||||
|
||||
## [0.4.7]
|
||||
### Fixed
|
||||
- Cameras everywhere
|
||||
### Changed
|
||||
- IndexedDB -> SQLite
|
||||
|
||||
|
||||
## [0.4.5] - 2025.02.23
|
||||
|
||||
@@ -1,170 +1,36 @@
|
||||
# TimeSafari Docker Build
|
||||
# Author: Matthew Raymer
|
||||
# Description: Multi-stage Docker build for TimeSafari web application
|
||||
#
|
||||
# Build Process:
|
||||
# 1. Base stage: Node.js with build dependencies
|
||||
# 2. Builder stage: Copy pre-built web assets from host
|
||||
# 3. Production stage: Nginx server with optimized assets
|
||||
#
|
||||
# Note: Web assets are built on the host using npm scripts before Docker build
|
||||
#
|
||||
# Security Features:
|
||||
# - Non-root user execution
|
||||
# - Minimal attack surface with Alpine Linux
|
||||
# - Multi-stage build to reduce image size
|
||||
# - No build dependencies in final image
|
||||
#
|
||||
# Usage:
|
||||
# IMPORTANT: Build web assets first, then build Docker image
|
||||
#
|
||||
# Using npm scripts (recommended):
|
||||
# Production: npm run build:web:docker:prod
|
||||
# Test: npm run build:web:docker:test
|
||||
# Development: npm run build:web:docker
|
||||
#
|
||||
# Manual workflow:
|
||||
# 1. Build web assets: npm run build:web:build -- --mode production
|
||||
# 2. Build Docker: docker build -t timesafari:latest .
|
||||
#
|
||||
# Note: For development, use npm run build:web directly (no Docker needed)
|
||||
#
|
||||
# Build Arguments:
|
||||
# BUILD_MODE: development, test, or production (default: production)
|
||||
# NODE_ENV: node environment (default: production)
|
||||
#
|
||||
# Environment Variables:
|
||||
# NODE_ENV: Build environment (development/production)
|
||||
# BUILD_MODE: Build mode for asset selection (development/test/production)
|
||||
#
|
||||
# Build Context:
|
||||
# This Dockerfile is designed to work when the build context is set to
|
||||
# ./crowd-funder-for-time-pwa from the parent directory (where docker-compose.yml is located)
|
||||
# Build stage
|
||||
FROM node:22-alpine3.20 AS builder
|
||||
|
||||
# =============================================================================
|
||||
# BASE STAGE - Common dependencies and setup
|
||||
# =============================================================================
|
||||
FROM node:22-alpine3.20 AS base
|
||||
# Install build dependencies
|
||||
|
||||
# Install system dependencies for build process
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
git \
|
||||
python3 \
|
||||
py3-pip \
|
||||
py3-setuptools \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Create non-root user for security
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nextjs -u 1001
|
||||
RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files for dependency installation
|
||||
# Note: These files are in the project root (crowd-funder-for-time-pwa directory)
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies with security audit
|
||||
RUN npm ci --only=production --audit --fund=false && \
|
||||
npm audit fix --audit-level=moderate || true
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# =============================================================================
|
||||
# BUILDER STAGE - Copy pre-built assets
|
||||
# =============================================================================
|
||||
FROM base AS builder
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Define build arguments with defaults
|
||||
ARG BUILD_MODE=production
|
||||
ARG NODE_ENV=production
|
||||
# Build the application
|
||||
RUN npm run build:web
|
||||
|
||||
# Set environment variables from build arguments
|
||||
ENV BUILD_MODE=${BUILD_MODE}
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
|
||||
# Copy pre-built assets from host
|
||||
# Note: dist/ directory is in the project root (crowd-funder-for-time-pwa directory)
|
||||
COPY dist/ ./dist/
|
||||
|
||||
# Verify build output exists
|
||||
RUN ls -la dist/ || (echo "Build output not found in dist/ directory" && exit 1)
|
||||
|
||||
# =============================================================================
|
||||
# PRODUCTION STAGE - Nginx server
|
||||
# =============================================================================
|
||||
FROM nginx:alpine AS production
|
||||
|
||||
# Define build arguments for production stage
|
||||
ARG BUILD_MODE=production
|
||||
ARG NODE_ENV=production
|
||||
|
||||
# Set environment variables
|
||||
ENV BUILD_MODE=${BUILD_MODE}
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
|
||||
# Install security updates and clean cache
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache \
|
||||
curl \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Use existing nginx user from base image (nginx user and group already exist)
|
||||
# No need to create new user as nginx:alpine already has nginx user
|
||||
|
||||
# Copy main nginx configuration
|
||||
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Copy production nginx configuration
|
||||
COPY docker/default.conf /etc/nginx/conf.d/default.conf
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy built assets from builder stage
|
||||
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Create necessary directories with proper permissions
|
||||
RUN mkdir -p /var/cache/nginx /var/log/nginx /tmp && \
|
||||
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /tmp && \
|
||||
chown -R nginx:nginx /usr/share/nginx/html
|
||||
|
||||
# Switch to non-root user
|
||||
USER nginx
|
||||
# Copy nginx configuration if needed
|
||||
# COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port 80
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost/ || exit 1
|
||||
|
||||
# Start nginx with proper signal handling
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
# =============================================================================
|
||||
# TEST STAGE - For test environment testing
|
||||
# =============================================================================
|
||||
FROM production AS test
|
||||
|
||||
# Define build arguments for test stage
|
||||
ARG BUILD_MODE=test
|
||||
ARG NODE_ENV=test
|
||||
|
||||
# Set environment variables
|
||||
ENV BUILD_MODE=${BUILD_MODE}
|
||||
ENV NODE_ENV=${NODE_ENV}
|
||||
|
||||
# Copy test-specific nginx configuration
|
||||
COPY docker/staging.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port 80
|
||||
EXPOSE 80
|
||||
|
||||
# Health check for staging
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost/health || exit 1
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -22,7 +22,26 @@ GEM
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.2)
|
||||
aws-partitions (1.1066.0)
|
||||
aws-sdk-core (3.220.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
base64
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.99.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.182.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.11.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
base64 (0.2.0)
|
||||
benchmark (0.4.0)
|
||||
bigdecimal (3.1.9)
|
||||
@@ -64,13 +83,96 @@ GEM
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.7.0)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (2.8.1)
|
||||
drb (2.2.1)
|
||||
emoji_regex (3.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
excon (0.112.0)
|
||||
faraday (1.10.4)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.1.0)
|
||||
multipart-post (~> 2.0)
|
||||
faraday-net_http (1.0.2)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.4.0)
|
||||
fastlane (2.227.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored (~> 1.2)
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
fastlane-sirp (>= 1.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
http-cookie (~> 1.0.5)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (>= 0.1.1, < 1.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.5)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (~> 3)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.4.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||
fastlane-sirp (1.0.0)
|
||||
sysrandom (~> 1.0)
|
||||
ffi (1.17.1)
|
||||
ffi (1.17.1-aarch64-linux-gnu)
|
||||
ffi (1.17.1-aarch64-linux-musl)
|
||||
@@ -85,27 +187,107 @@ GEM
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.54.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.3)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.31.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.8.0)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.5.0)
|
||||
google-cloud-storage (1.47.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.31.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.8.1)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.8)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.9.0)
|
||||
mutex_m
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.10.2)
|
||||
jwt (2.10.1)
|
||||
base64
|
||||
logger (1.6.6)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.25.5)
|
||||
molinillo (0.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.1)
|
||||
mutex_m (0.3.0)
|
||||
nanaimo (0.4.0)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.1)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
optparse (0.6.0)
|
||||
os (1.1.4)
|
||||
plist (3.7.2)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.2.1)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.4.1)
|
||||
rouge (3.28.0)
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.4.1)
|
||||
securerandom (0.4.1)
|
||||
security (0.1.5)
|
||||
signet (0.19.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
sysrandom (1.0.5)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.2)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (2.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.27.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
@@ -113,6 +295,10 @@ GEM
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.4.0)
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
xcpretty (0.4.0)
|
||||
rouge (~> 3.28.0)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux-gnu
|
||||
@@ -129,6 +315,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.6.5
|
||||
|
||||
@@ -3,32 +3,6 @@
|
||||
[Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude
|
||||
and expand to crowd-fund with time & money, then record and see the impact of contributions.
|
||||
|
||||
## Database Migration Status
|
||||
|
||||
**Current Status**: The application is undergoing a migration from Dexie (IndexedDB) to SQLite using absurd-sql. This migration is in **Phase 2** with a well-defined migration fence in place.
|
||||
|
||||
### Migration Progress
|
||||
- ✅ **SQLite Database Service**: Fully implemented with absurd-sql
|
||||
- ✅ **Platform Service Layer**: Unified database interface across platforms
|
||||
- ✅ **Settings Migration**: Core user settings transferred
|
||||
- ✅ **Account Migration**: Identity and key management
|
||||
- 🔄 **Contact Migration**: User contact data (via import interface)
|
||||
- 📋 **Code Cleanup**: Remove unused Dexie imports
|
||||
|
||||
### Migration Fence
|
||||
The migration is controlled by a **migration fence** that separates legacy Dexie code from the new SQLite implementation. See [Migration Fence Definition](doc/migration-fence-definition.md) for complete details.
|
||||
|
||||
**Key Points**:
|
||||
- Legacy Dexie database is disabled by default
|
||||
- All database operations go through `PlatformServiceMixin`
|
||||
- Migration tools provide controlled access to both databases
|
||||
- Clear separation between legacy and new code
|
||||
|
||||
### Migration Documentation
|
||||
- [Migration Guide](doc/migration-to-wa-sqlite.md) - Complete migration process
|
||||
- [Migration Fence Definition](doc/migration-fence-definition.md) - Fence boundaries and rules
|
||||
- [Database Migration Guide](doc/database-migration-guide.md) - User-facing migration tools
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [project.task.yaml](project.task.yaml) for current priorities.
|
||||
@@ -45,92 +19,18 @@ npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker).
|
||||
See [BUILDING.md](BUILDING.md) for more details.
|
||||
|
||||
## Development Database Clearing
|
||||
|
||||
TimeSafari provides a simple script-based approach to clear the database for development purposes.
|
||||
|
||||
### Quick Usage
|
||||
```bash
|
||||
# Run the database clearing script
|
||||
./scripts/clear-database.sh
|
||||
|
||||
# Then restart your development server
|
||||
npm run build:electron:dev # For Electron
|
||||
npm run build:web:dev # For Web
|
||||
```
|
||||
|
||||
### What It Does
|
||||
|
||||
#### **Electron (Desktop App)**
|
||||
- Automatically finds and clears the SQLite database files
|
||||
- Works on Linux, macOS, and Windows
|
||||
- Clears all data and forces fresh migrations on next startup
|
||||
|
||||
#### **Web Browser**
|
||||
- Provides instructions for using custom browser data directories
|
||||
- Shows manual clearing via browser DevTools
|
||||
- Ensures reliable database clearing without browser complications
|
||||
|
||||
### Safety Features
|
||||
- ✅ **Interactive Script**: Guides you through the process
|
||||
- ✅ **Platform Detection**: Automatically detects your OS
|
||||
- ✅ **Clear Instructions**: Step-by-step guidance for each platform
|
||||
- ✅ **Safe Paths**: Only clears TimeSafari-specific data
|
||||
|
||||
### Manual Commands (if needed)
|
||||
|
||||
#### **Electron Database Location**
|
||||
```bash
|
||||
# Linux
|
||||
rm -rf ~/.config/TimeSafari/*
|
||||
|
||||
# macOS
|
||||
rm -rf ~/Library/Application\ Support/TimeSafari/*
|
||||
|
||||
# Windows
|
||||
rmdir /s /q %APPDATA%\TimeSafari
|
||||
```
|
||||
|
||||
#### **Web Browser (Custom Data Directory)**
|
||||
```bash
|
||||
# Create isolated browser profile
|
||||
mkdir ~/timesafari-dev-data
|
||||
```
|
||||
|
||||
## Domain Configuration
|
||||
|
||||
TimeSafari uses a centralized domain configuration system to ensure consistent
|
||||
URL generation across all environments. This prevents localhost URLs from
|
||||
appearing in shared links during development.
|
||||
|
||||
### Key Features
|
||||
- ✅ **Production URLs for Sharing**: All copy link buttons use production domain
|
||||
- ✅ **Environment-Specific Internal URLs**: Internal operations use appropriate
|
||||
environment URLs
|
||||
- ✅ **Single Point of Control**: Change domain in one place for entire app
|
||||
- ✅ **Type-Safe Configuration**: Full TypeScript support
|
||||
|
||||
### Quick Reference
|
||||
```typescript
|
||||
// For sharing functionality (always production)
|
||||
import { PROD_SHARE_DOMAIN } from "@/constants/app";
|
||||
const shareLink = `${PROD_SHARE_DOMAIN}/deep-link/claim/123`;
|
||||
|
||||
// For internal operations (environment-specific)
|
||||
import { APP_SERVER } from "@/constants/app";
|
||||
const apiUrl = `${APP_SERVER}/api/claim/123`;
|
||||
```
|
||||
|
||||
### Documentation
|
||||
- [Domain Configuration System](docs/domain-configuration.md) - Complete guide
|
||||
- [Constants and Configuration](src/constants/app.ts) - Core constants
|
||||
|
||||
## Tests
|
||||
|
||||
See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions.
|
||||
|
||||
|
||||
|
||||
|
||||
## Icons
|
||||
|
||||
Application icons are in the `assets` directory, processed by the `capacitor-assets` command.
|
||||
@@ -166,25 +66,6 @@ Key principles:
|
||||
- Common interfaces are shared through `common.ts`
|
||||
- Type definitions are generated from Zod schemas where possible
|
||||
|
||||
### Database Architecture
|
||||
|
||||
The application uses a platform-agnostic database layer with Vue mixins for service access:
|
||||
|
||||
* `src/services/PlatformService.ts` - Database interface definition
|
||||
* `src/services/PlatformServiceFactory.ts` - Platform-specific service factory
|
||||
* `src/services/AbsurdSqlDatabaseService.ts` - SQLite implementation
|
||||
* `src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching
|
||||
* `src/db/` - Legacy Dexie database (migration in progress)
|
||||
|
||||
**Development Guidelines**:
|
||||
|
||||
- Always use `PlatformServiceMixin` for database operations in components
|
||||
- Test with PlatformServiceMixin for new features
|
||||
- Use migration tools for data transfer between systems
|
||||
- Leverage mixin's ultra-concise methods: `$db()`, `$exec()`, `$one()`, `$contacts()`, `$settings()`
|
||||
|
||||
**Architecture Decision**: The project uses Vue mixins over Composition API composables for platform service access. See [Architecture Decisions](doc/architecture-decisions.md) for detailed rationale.
|
||||
|
||||
### Kudos
|
||||
|
||||
Gifts make the world go 'round!
|
||||
|
||||
@@ -84,6 +84,13 @@ freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ android {
|
||||
applicationId "app.timesafari.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 35
|
||||
versionName "1.0.2"
|
||||
versionCode 18
|
||||
versionName "0.4.7"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
android:exported="true"
|
||||
android:label="@string/title_activity_main"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBarLaunch">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"appId": "app.timesafari",
|
||||
"appName": "TimeSafari",
|
||||
"webDir": "dist",
|
||||
"bundledWebRuntime": false,
|
||||
"server": {
|
||||
"cleartext": true
|
||||
},
|
||||
@@ -16,36 +17,22 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"SplashScreen": {
|
||||
"launchShowDuration": 3000,
|
||||
"launchAutoHide": true,
|
||||
"backgroundColor": "#ffffff",
|
||||
"androidSplashResourceName": "splash",
|
||||
"androidScaleType": "CENTER_CROP",
|
||||
"showSpinner": false,
|
||||
"androidSpinnerStyle": "large",
|
||||
"iosSpinnerStyle": "small",
|
||||
"spinnerColor": "#999999",
|
||||
"splashFullScreen": true,
|
||||
"splashImmersive": true
|
||||
},
|
||||
"CapacitorSQLite": {
|
||||
"SQLite": {
|
||||
"iosDatabaseLocation": "Library/CapacitorDatabase",
|
||||
"iosIsEncryption": false,
|
||||
"iosIsEncryption": true,
|
||||
"iosBiometric": {
|
||||
"biometricAuth": false,
|
||||
"biometricAuth": true,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
},
|
||||
"androidIsEncryption": false,
|
||||
"androidIsEncryption": true,
|
||||
"androidBiometric": {
|
||||
"biometricAuth": false,
|
||||
"biometricAuth": true,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
},
|
||||
"electronIsEncryption": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"contentInset": "never",
|
||||
"contentInset": "always",
|
||||
"allowsLinkPreview": true,
|
||||
"scrollEnabled": true,
|
||||
"limitsNavigationsToAppBoundDomains": true,
|
||||
@@ -65,56 +52,5 @@
|
||||
"*.jsdelivr.net",
|
||||
"api.endorser.ch"
|
||||
]
|
||||
},
|
||||
"electron": {
|
||||
"deepLinking": {
|
||||
"schemes": [
|
||||
"timesafari"
|
||||
]
|
||||
},
|
||||
"buildOptions": {
|
||||
"appId": "app.timesafari",
|
||||
"productName": "TimeSafari",
|
||||
"directories": {
|
||||
"output": "dist-electron-packages"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"electron/**/*"
|
||||
],
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "Utility"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 463 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M2480 4005 c-25 -7 -58 -20 -75 -29 -16 -9 -40 -16 -52 -16 -17 0
|
||||
-24 -7 -28 -27 -3 -16 -14 -45 -24 -65 -21 -41 -13 -55 18 -38 25 13 67 13 92
|
||||
-1 15 -8 35 -4 87 17 99 39 130 41 197 10 64 -29 77 -31 107 -15 20 11 20 11
|
||||
-3 35 -12 13 -30 24 -38 24 -24 1 -132 38 -148 51 -8 7 -11 20 -7 32 12 37
|
||||
-40 47 -126 22z"/>
|
||||
<path d="M1450 3775 c-7 -8 -18 -15 -24 -15 -7 0 -31 -14 -54 -32 -29 -22 -38
|
||||
-34 -29 -40 17 -11 77 -10 77 1 0 5 16 16 35 25 60 29 220 19 290 -18 17 -9
|
||||
33 -16 37 -16 4 0 31 -15 60 -34 108 -70 224 -215 282 -353 30 -71 53 -190 42
|
||||
-218 -10 -27 -23 -8 -52 75 -30 90 -88 188 -120 202 -13 6 -26 9 -29 6 -3 -2
|
||||
11 -51 30 -108 28 -83 35 -119 35 -179 0 -120 -22 -127 -54 -17 -11 37 -13 21
|
||||
-18 -154 -5 -180 -8 -200 -32 -264 -51 -132 -129 -245 -199 -288 -21 -12 -79
|
||||
-49 -129 -80 -161 -102 -294 -141 -473 -141 -228 0 -384 76 -535 259 -81 99
|
||||
-118 174 -154 312 -31 121 -35 273 -11 437 19 127 19 125 -4 125 -23 0 -51
|
||||
-34 -87 -104 -14 -28 -33 -64 -41 -81 -19 -34 -22 -253 -7 -445 9 -106 12
|
||||
-119 44 -170 19 -30 42 -67 50 -81 64 -113 85 -140 130 -169 28 -18 53 -44 61
|
||||
-62 8 -20 36 -45 83 -76 62 -39 80 -46 151 -54 44 -5 96 -13 115 -18 78 -20
|
||||
238 -31 282 -19 24 6 66 8 95 5 76 -9 169 24 319 114 32 19 80 56 106 82 27
|
||||
26 52 48 58 48 5 0 27 26 50 58 48 66 56 70 132 71 62 1 165 29 238 64 112 55
|
||||
177 121 239 245 37 76 39 113 10 267 -12 61 -23 131 -26 156 -5 46 -5 47 46
|
||||
87 92 73 182 70 263 -8 l51 -49 -6 -61 c-4 -34 -13 -85 -21 -113 -28 -103 -30
|
||||
-161 -4 -228 16 -44 32 -67 55 -83 18 -11 39 -37 47 -58 10 -23 37 -53 73 -81
|
||||
32 -25 69 -57 82 -71 14 -14 34 -26 47 -26 12 0 37 -7 56 -15 20 -8 66 -17
|
||||
104 -20 107 -10 110 -11 150 -71 50 -75 157 -177 197 -187 18 -5 53 -24 78
|
||||
-42 71 -51 176 -82 304 -89 61 -4 127 -12 147 -18 29 -9 45 -8 77 6 23 9 50
|
||||
16 60 16 31 0 163 46 216 76 28 15 75 46 105 69 30 23 69 49 85 58 17 8 46 31
|
||||
64 51 19 20 40 36 47 36 18 0 77 70 100 120 32 66 45 108 55 173 5 32 16 71
|
||||
24 87 43 84 43 376 0 549 -27 105 -43 127 -135 188 -30 21 -65 46 -77 57 -13
|
||||
11 -23 17 -23 14 0 -3 21 -46 47 -94 79 -151 85 -166 115 -263 25 -83 28 -110
|
||||
28 -226 0 -144 -17 -221 -75 -335 -39 -77 -208 -244 -304 -299 -451 -263 -975
|
||||
-67 -1138 426 -23 70 -26 95 -28 254 -1 108 -7 183 -14 196 -6 12 -11 31 -11
|
||||
43 0 32 31 122 52 149 10 13 18 28 18 34 0 5 25 40 56 78 60 73 172 170 219
|
||||
190 30 12 30 13 6 17 -15 2 -29 -2 -37 -12 -6 -9 -16 -16 -22 -16 -6 0 -23
|
||||
-11 -39 -24 -15 -12 -33 -25 -40 -27 -17 -6 -82 -60 -117 -97 -65 -70 -75 -82
|
||||
-107 -133 -23 -34 -35 -46 -37 -35 -3 16 20 87 44 134 6 12 9 34 6 48 -4 22
|
||||
-8 25 -31 19 -14 -3 -38 -15 -53 -26 -34 -24 -34 -21 -6 28 65 112 184 206
|
||||
291 227 15 3 39 9 55 12 l27 6 -24 9 c-90 35 -304 -66 -478 -225 -39 -36 -74
|
||||
-66 -77 -66 -22 0 18 82 72 148 19 23 32 46 28 49 -4 4 -26 13 -49 19 -73 21
|
||||
-161 54 -171 64 -6 6 -20 10 -32 10 -21 0 -21 -1 -8 -40 45 -130 8 -247 -93
|
||||
-299 -25 -13 -31 0 -14 29 15 22 1 33 -22 17 -56 -36 -117 -22 -117 28 0 13
|
||||
-16 47 -35 76 -22 34 -33 60 -29 73 4 16 -3 26 -26 39 -16 10 -30 21 -30 25 1
|
||||
18 54 64 87 76 l38 13 -33 5 c-30 4 -115 -18 -154 -42 -13 -7 -20 -5 -27 8 -9
|
||||
16 -12 16 -53 1 -160 -61 -258 -104 -258 -114 0 -7 10 -20 21 -31 103 -91 217
|
||||
-297 249 -449 28 -135 41 -237 35 -276 -14 -91 -48 -170 -97 -220 -44 -47 -68
|
||||
-60 -68 -40 0 6 4 12 8 15 5 3 24 35 42 72 l33 67 -6 141 c-4 103 -11 158 -26
|
||||
205 -12 35 -21 70 -21 77 0 7 -20 56 -45 108 -82 173 -227 322 -392 401 -67
|
||||
33 -90 39 -163 42 -108 5 -130 10 -130 28 0 20 -63 20 -80 0z"/>
|
||||
<path d="M3710 3765 c0 -20 8 -28 39 -41 22 -8 42 -22 45 -30 5 -14 42 -19 70
|
||||
-8 10 4 -7 21 -58 55 -41 27 -79 49 -85 49 -6 0 -11 -11 -11 -25z"/>
|
||||
<path d="M3173 3734 c-9 -25 10 -36 35 -18 12 8 22 19 22 25 0 16 -50 10 -57
|
||||
-7z"/>
|
||||
<path d="M1982 3728 c6 -16 36 -34 44 -26 3 4 4 14 1 23 -7 17 -51 21 -45 3z"/>
|
||||
<path d="M1540 3620 c0 -5 7 -10 16 -10 8 0 12 5 9 10 -3 6 -10 10 -16 10 -5
|
||||
0 -9 -4 -9 -10z"/>
|
||||
<path d="M4467 3624 c-4 -4 23 -27 60 -50 84 -56 99 -58 67 -9 -28 43 -107 79
|
||||
-127 59z"/>
|
||||
<path d="M655 3552 c-11 -2 -26 -9 -33 -14 -7 -6 -27 -18 -45 -27 -36 -18 -58
|
||||
-64 -39 -83 9 -9 25 1 70 43 53 48 78 78 70 84 -2 1 -12 -1 -23 -3z"/>
|
||||
<path d="M1015 3460 c-112 -24 -247 -98 -303 -165 -53 -65 -118 -214 -136
|
||||
-311 -20 -113 -20 -145 -1 -231 20 -88 49 -153 102 -230 79 -113 186 -182 331
|
||||
-214 108 -24 141 -24 247 1 130 30 202 72 316 181 102 100 153 227 152 384 0
|
||||
142 -58 293 -150 395 -60 67 -180 145 -261 171 -75 23 -232 34 -297 19z m340
|
||||
-214 c91 -43 174 -154 175 -234 0 -18 -9 -51 -21 -73 -19 -37 -19 -42 -5 -64
|
||||
35 -54 12 -121 -48 -142 -22 -7 -47 -19 -55 -27 -9 -8 -41 -27 -71 -42 -50
|
||||
-26 -64 -29 -155 -29 -111 0 -152 14 -206 68 -49 49 -63 85 -64 162 0 59 4 78
|
||||
28 118 31 52 96 105 141 114 23 5 33 17 56 68 46 103 121 130 225 81z"/>
|
||||
<path d="M3985 3464 c-44 -7 -154 -44 -200 -67 -55 -28 -138 -96 -162 -132
|
||||
-10 -16 -39 -75 -64 -130 l-44 -100 0 -160 0 -160 45 -90 c53 -108 152 -214
|
||||
245 -264 59 -31 215 -71 281 -71 53 0 206 40 255 67 98 53 203 161 247 253 53
|
||||
113 74 193 74 280 -1 304 -253 564 -557 575 -49 2 -103 1 -120 -1z m311 -220
|
||||
c129 -68 202 -209 160 -309 -15 -35 -15 -42 -1 -72 26 -55 -3 -118 -59 -129
|
||||
-19 -3 -43 -15 -53 -26 -26 -29 -99 -64 -165 -78 -45 -10 -69 -10 -120 -1 -74
|
||||
15 -113 37 -161 91 -110 120 -50 331 109 385 24 8 44 23 52 39 6 14 18 38 25
|
||||
53 33 72 127 93 213 47z"/>
|
||||
<path d="M487 3394 c-21 -12 -27 -21 -25 -40 2 -14 7 -26 12 -27 14 -3 48 48
|
||||
44 66 -3 14 -6 14 -31 1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 705 KiB |
@@ -0,0 +1,11 @@
|
||||
Model Information:
|
||||
* title: Lupine Plant
|
||||
* source: https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439
|
||||
* author: rufusrockwell (https://sketchfab.com/rufusrockwell)
|
||||
|
||||
Model License:
|
||||
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||
* requirements: Author must be credited. Commercial use is allowed.
|
||||
|
||||
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
|
||||
This work is based on "Lupine Plant" (https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) by rufusrockwell (https://sketchfab.com/rufusrockwell) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
||||
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 2,
|
||||
"componentType": 5126,
|
||||
"count": 2759,
|
||||
"max": [
|
||||
41.3074951171875,
|
||||
40.37548828125,
|
||||
87.85917663574219
|
||||
],
|
||||
"min": [
|
||||
-35.245540618896484,
|
||||
-36.895416259765625,
|
||||
-0.9094290137290955
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"byteOffset": 33108,
|
||||
"componentType": 5126,
|
||||
"count": 2759,
|
||||
"max": [
|
||||
0.9999382495880127,
|
||||
0.9986748695373535,
|
||||
0.9985831379890442
|
||||
],
|
||||
"min": [
|
||||
-0.9998949766159058,
|
||||
-0.9975876212120056,
|
||||
-0.411094069480896
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"componentType": 5126,
|
||||
"count": 2759,
|
||||
"max": [
|
||||
0.9987699389457703,
|
||||
0.9998998045921326,
|
||||
0.9577858448028564,
|
||||
1.0
|
||||
],
|
||||
"min": [
|
||||
-0.9987726807594299,
|
||||
-0.9990445971488953,
|
||||
-0.999801516532898,
|
||||
1.0
|
||||
],
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"componentType": 5126,
|
||||
"count": 2759,
|
||||
"max": [
|
||||
1.0061479806900024,
|
||||
0.9993550181388855
|
||||
],
|
||||
"min": [
|
||||
0.00279300007969141,
|
||||
0.0011620000004768372
|
||||
],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5125,
|
||||
"count": 6378,
|
||||
"type": "SCALAR"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"extras": {
|
||||
"author": "rufusrockwell (https://sketchfab.com/rufusrockwell)",
|
||||
"license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
|
||||
"source": "https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439",
|
||||
"title": "Lupine Plant"
|
||||
},
|
||||
"generator": "Sketchfab-12.68.0",
|
||||
"version": "2.0"
|
||||
},
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 25512,
|
||||
"name": "floatBufferViews",
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 22072,
|
||||
"byteOffset": 25512,
|
||||
"byteStride": 8,
|
||||
"name": "floatBufferViews",
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 66216,
|
||||
"byteOffset": 47584,
|
||||
"byteStride": 12,
|
||||
"name": "floatBufferViews",
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 44144,
|
||||
"byteOffset": 113800,
|
||||
"byteStride": 16,
|
||||
"name": "floatBufferViews",
|
||||
"target": 34962
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 157944,
|
||||
"uri": "scene.bin"
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": "textures/lambert2SG_baseColor.png"
|
||||
},
|
||||
{
|
||||
"uri": "textures/lambert2SG_normal.png"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"alphaCutoff": 0.2,
|
||||
"alphaMode": "MASK",
|
||||
"doubleSided": true,
|
||||
"name": "lambert2SG",
|
||||
"normalTexture": {
|
||||
"index": 1
|
||||
},
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0
|
||||
},
|
||||
"metallicFactor": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"name": "Object_0",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 1,
|
||||
"POSITION": 0,
|
||||
"TANGENT": 2,
|
||||
"TEXCOORD_0": 3
|
||||
},
|
||||
"indices": 4,
|
||||
"material": 0,
|
||||
"mode": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"children": [
|
||||
1
|
||||
],
|
||||
"matrix": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
2.220446049250313e-16,
|
||||
-1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
2.220446049250313e-16,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"name": "Sketchfab_model"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
2
|
||||
],
|
||||
"name": "LupineSF.obj.cleaner.materialmerger.gles"
|
||||
},
|
||||
{
|
||||
"mesh": 0,
|
||||
"name": "Object_2"
|
||||
}
|
||||
],
|
||||
"samplers": [
|
||||
{
|
||||
"magFilter": 9729,
|
||||
"minFilter": 9987,
|
||||
"wrapS": 10497,
|
||||
"wrapT": 10497
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"name": "Sketchfab_Scene",
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"sampler": 0,
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"sampler": 0,
|
||||
"source": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -2,7 +2,7 @@ package app.timesafari;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
//import com.getcapacitor.community.sqlite.SQLite;
|
||||
import com.getcapacitor.community.sqlite.SQLite;
|
||||
|
||||
public class MainActivity extends BridgeActivity {
|
||||
@Override
|
||||
@@ -10,6 +10,6 @@ public class MainActivity extends BridgeActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Initialize SQLite
|
||||
//registerPlugin(SQLite.class);
|
||||
registerPlugin(SQLite.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package timesafari.app;
|
||||
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
public class MainActivity extends BridgeActivity {}
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.11.1'
|
||||
classpath 'com.android.tools.build:gradle:8.9.1'
|
||||
classpath 'com.google.gms:google-services:4.4.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
|
After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.9 MiB |
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"icon": {
|
||||
"ios": {
|
||||
"source": "resources/ios/icon/icon.png",
|
||||
"target": "ios/App/App/Assets.xcassets/AppIcon.appiconset"
|
||||
},
|
||||
"android": {
|
||||
"source": "resources/android/icon/icon.png",
|
||||
"target": "android/app/src/main/res"
|
||||
},
|
||||
"web": {
|
||||
"source": "resources/web/icon/icon.png",
|
||||
"target": "public/img/icons"
|
||||
}
|
||||
},
|
||||
"splash": {
|
||||
"ios": {
|
||||
"source": "resources/ios/splash/splash.png",
|
||||
"target": "ios/App/App/Assets.xcassets/Splash.imageset"
|
||||
},
|
||||
"android": {
|
||||
"source": "resources/android/splash/splash.png",
|
||||
"target": "android/app/src/main/res"
|
||||
}
|
||||
},
|
||||
"splashDark": {
|
||||
"ios": {
|
||||
"source": "resources/ios/splash/splash_dark.png",
|
||||
"target": "ios/App/App/Assets.xcassets/SplashDark.imageset"
|
||||
},
|
||||
"android": {
|
||||
"source": "resources/android/splash/splash_dark.png",
|
||||
"target": "android/app/src/main/res"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"appId": "app.timesafari",
|
||||
"appId": "com.timesafari.app",
|
||||
"appName": "TimeSafari",
|
||||
"webDir": "dist",
|
||||
"bundledWebRuntime": false,
|
||||
"server": {
|
||||
"cleartext": true
|
||||
"cleartext": true,
|
||||
"androidScheme": "https"
|
||||
},
|
||||
"plugins": {
|
||||
"App": {
|
||||
@@ -16,36 +18,28 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"SplashScreen": {
|
||||
"launchShowDuration": 3000,
|
||||
"launchAutoHide": true,
|
||||
"backgroundColor": "#ffffff",
|
||||
"androidSplashResourceName": "splash",
|
||||
"androidScaleType": "CENTER_CROP",
|
||||
"showSpinner": false,
|
||||
"androidSpinnerStyle": "large",
|
||||
"iosSpinnerStyle": "small",
|
||||
"spinnerColor": "#999999",
|
||||
"splashFullScreen": true,
|
||||
"splashImmersive": true
|
||||
"SQLite": {
|
||||
"iosDatabaseLocation": "Library/CapacitorDatabase",
|
||||
"iosIsEncryption": true,
|
||||
"iosBiometric": {
|
||||
"biometricAuth": true,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
},
|
||||
"androidIsEncryption": true,
|
||||
"androidBiometric": {
|
||||
"biometricAuth": true,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
}
|
||||
},
|
||||
"CapacitorSQLite": {
|
||||
"iosDatabaseLocation": "Library/CapacitorDatabase",
|
||||
"iosIsEncryption": false,
|
||||
"iosBiometric": {
|
||||
"biometricAuth": false,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
},
|
||||
"androidIsEncryption": false,
|
||||
"androidBiometric": {
|
||||
"biometricAuth": false,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
},
|
||||
"electronIsEncryption": false
|
||||
"electronIsEncryption": false,
|
||||
"electronMacLocation": "~/Library/Application Support/TimeSafari",
|
||||
"electronWindowsLocation": "C:\\ProgramData\\TimeSafari",
|
||||
"electronLinuxLocation": "~/.local/share/TimeSafari"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"contentInset": "never",
|
||||
"contentInset": "always",
|
||||
"allowsLinkPreview": true,
|
||||
"scrollEnabled": true,
|
||||
"limitsNavigationsToAppBoundDomains": true,
|
||||
@@ -65,47 +59,5 @@
|
||||
"*.jsdelivr.net",
|
||||
"api.endorser.ch"
|
||||
]
|
||||
},
|
||||
"electron": {
|
||||
"deepLinking": {
|
||||
"schemes": ["timesafari"]
|
||||
},
|
||||
"buildOptions": {
|
||||
"appId": "app.timesafari",
|
||||
"productName": "TimeSafari",
|
||||
"directories": {
|
||||
"output": "dist-electron-packages"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"electron/**/*"
|
||||
],
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
"arch": ["x64", "arm64"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
],
|
||||
"category": "Utility"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,6 @@ try {
|
||||
- `src/interfaces/deepLinks.ts`: Type definitions and validation schemas
|
||||
- `src/services/deepLinks.ts`: Deep link processing service
|
||||
- `src/main.capacitor.ts`: Capacitor integration
|
||||
- `src/views/DeepLinkRedirectView.vue`: Page to handle links to both mobile and web
|
||||
|
||||
## Type Safety Examples
|
||||
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
# Worker-Only Database Implementation for Web Platform
|
||||
|
||||
## Overview
|
||||
|
||||
This implementation fixes the double migration issue in the TimeSafari web platform by implementing worker-only database access, similar to the Capacitor platform architecture.
|
||||
|
||||
## Problem Solved
|
||||
|
||||
**Before:** Web platform had dual database contexts:
|
||||
|
||||
- Worker thread: `registerSQLWorker.js` → `AbsurdSqlDatabaseService.initialize()` → migrations run
|
||||
- Main thread: `WebPlatformService.dbQuery()` → `databaseService.query()` → migrations run **AGAIN**
|
||||
|
||||
**After:** Single database context:
|
||||
|
||||
- Worker thread: Handles ALL database operations and initializes once
|
||||
- Main thread: Sends messages to worker, no direct database access
|
||||
|
||||
## Architecture Changes
|
||||
|
||||
### 1. Message-Based Communication
|
||||
|
||||
```typescript
|
||||
// Main Thread (WebPlatformService)
|
||||
await this.sendWorkerMessage<QueryResult>({
|
||||
type: "query",
|
||||
sql: "SELECT * FROM users",
|
||||
params: []
|
||||
});
|
||||
|
||||
// Worker Thread (registerSQLWorker.js)
|
||||
onmessage = async (event) => {
|
||||
const { id, type, sql, params } = event.data;
|
||||
if (type === "query") {
|
||||
const result = await databaseService.query(sql, params);
|
||||
postMessage({ id, type: "success", data: { result } });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Type-Safe Worker Messages
|
||||
|
||||
```typescript
|
||||
// src/interfaces/worker-messages.ts
|
||||
export interface QueryRequest extends BaseWorkerMessage {
|
||||
type: "query";
|
||||
sql: string;
|
||||
params?: unknown[];
|
||||
}
|
||||
|
||||
export type WorkerRequest =
|
||||
| QueryRequest
|
||||
| ExecRequest
|
||||
| GetOneRowRequest
|
||||
| InitRequest
|
||||
| PingRequest;
|
||||
```
|
||||
|
||||
### 3. Circular Dependency Resolution
|
||||
|
||||
#### 🔥 Critical Fix: Stack Overflow Prevention
|
||||
|
||||
**Problem**: Circular module dependency caused infinite recursion:
|
||||
|
||||
- `WebPlatformService` constructor → creates Worker
|
||||
- Worker loads `registerSQLWorker.js` → imports `databaseService`
|
||||
- Module resolution creates circular dependency → Stack Overflow
|
||||
|
||||
**Solution**: Lazy Loading in Worker
|
||||
|
||||
```javascript
|
||||
// Before (caused stack overflow)
|
||||
import databaseService from "./services/AbsurdSqlDatabaseService";
|
||||
|
||||
// After (fixed)
|
||||
let databaseService = null;
|
||||
|
||||
async function getDatabaseService() {
|
||||
if (!databaseService) {
|
||||
// Dynamic import prevents circular dependency
|
||||
const { default: service } = await import("./services/AbsurdSqlDatabaseService");
|
||||
databaseService = service;
|
||||
}
|
||||
return databaseService;
|
||||
}
|
||||
```
|
||||
|
||||
**Key Changes for Stack Overflow Fix:**
|
||||
|
||||
- ✅ Removed top-level import of database service
|
||||
- ✅ Added lazy loading with dynamic import
|
||||
- ✅ Updated all handlers to use `await getDatabaseService()`
|
||||
- ✅ Removed auto-initialization that triggered immediate loading
|
||||
- ✅ Database service only loads when first database operation occurs
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### 1. WebPlatformService Changes
|
||||
|
||||
- Removed direct database imports
|
||||
- Added worker message handling
|
||||
- Implemented timeout and error handling
|
||||
- All database methods now proxy to worker
|
||||
|
||||
### 2. Worker Thread Changes
|
||||
|
||||
- Added message-based operation handling
|
||||
- Implemented lazy loading for database service
|
||||
- Added proper error handling and response formatting
|
||||
- Fixed circular dependency with dynamic imports
|
||||
|
||||
### 3. Main Thread Changes
|
||||
|
||||
- Removed duplicate worker creation in `main.web.ts`
|
||||
- WebPlatformService now manages single worker instance
|
||||
- Added Safari compatibility with `initBackend()`
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. **src/interfaces/worker-messages.ts** *(NEW)*
|
||||
- Type definitions for worker communication
|
||||
- Request and response message interfaces
|
||||
|
||||
2. **src/registerSQLWorker.js** *(MAJOR REWRITE)*
|
||||
- Message-based operation handling
|
||||
- **Fixed circular dependency with lazy loading**
|
||||
- Proper error handling and response formatting
|
||||
|
||||
3. **src/services/platforms/WebPlatformService.ts** *(MAJOR REWRITE)*
|
||||
- Worker-only database access
|
||||
- Message sending and response handling
|
||||
- Timeout and error management
|
||||
|
||||
4. **src/main.web.ts** *(SIMPLIFIED)*
|
||||
- Removed duplicate worker creation
|
||||
- Simplified initialization flow
|
||||
|
||||
5. **WORKER_ONLY_DATABASE_IMPLEMENTATION.md** *(NEW)*
|
||||
- Complete documentation of changes
|
||||
|
||||
## Benefits
|
||||
|
||||
### ✅ Fixes Double Migration Issue
|
||||
|
||||
- Database migrations now run only once in worker thread
|
||||
- No duplicate initialization between main thread and worker
|
||||
|
||||
### ✅ Prevents Stack Overflow
|
||||
|
||||
- Circular dependency resolved with lazy loading
|
||||
- Worker loads immediately without triggering database import
|
||||
- Database service loads on-demand when first operation occurs
|
||||
|
||||
### ✅ Improved Performance
|
||||
|
||||
- Single database connection
|
||||
- No redundant operations
|
||||
- Better resource utilization
|
||||
|
||||
### ✅ Better Error Handling
|
||||
|
||||
- Centralized error handling in worker
|
||||
- Type-safe message communication
|
||||
- Proper timeout handling
|
||||
|
||||
### ✅ Consistent Architecture
|
||||
|
||||
- Matches Capacitor platform pattern
|
||||
- Single-threaded database access
|
||||
- Clear separation of concerns
|
||||
|
||||
## Testing Verification
|
||||
|
||||
After implementation, you should see:
|
||||
|
||||
1. **Worker Loading**:
|
||||
|
||||
```text
|
||||
[SQLWorker] Worker loaded, ready to receive messages
|
||||
```
|
||||
|
||||
2. **Database Initialization** (only on first operation):
|
||||
|
||||
```text
|
||||
[SQLWorker] Starting database initialization...
|
||||
[SQLWorker] Database initialization completed successfully
|
||||
```
|
||||
|
||||
3. **No Stack Overflow**: Application starts without infinite recursion
|
||||
4. **Single Migration Run**: Database migrations execute only once
|
||||
5. **Functional Database**: All queries, inserts, and updates work correctly
|
||||
|
||||
## Migration from Previous Implementation
|
||||
|
||||
If upgrading from the dual-context implementation:
|
||||
|
||||
1. **Remove Direct Database Imports**: No more `import databaseService` in main thread
|
||||
2. **Update Database Calls**: Use platform service methods instead of direct database calls
|
||||
3. **Handle Async Operations**: All database operations are now async message-based
|
||||
4. **Error Handling**: Update error handling to work with worker responses
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Worker thread isolates database operations
|
||||
- Message validation prevents malformed requests
|
||||
- Timeout handling prevents hanging operations
|
||||
- Type safety reduces runtime errors
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- Initial worker creation has minimal overhead
|
||||
- Database operations have message passing overhead (negligible)
|
||||
- Single database connection is more efficient than dual connections
|
||||
- Lazy loading reduces startup time
|
||||
|
||||
## Migration Execution Flow
|
||||
|
||||
### Before (Problematic)
|
||||
|
||||
```chart
|
||||
┌────────────── ───┐ ┌─────────────────┐
|
||||
│ Main Thread │ │ Worker Thread │
|
||||
│ │ │ │
|
||||
│ WebPlatformService│ │registerSQLWorker│
|
||||
│ ↓ │ │ ↓ │
|
||||
│ databaseService │ │ databaseService │
|
||||
│ (Instance A) │ │ (Instance B) │
|
||||
│ ↓ │ │ ↓ │
|
||||
│ [Run Migrations] │ │[Run Migrations] │ ← DUPLICATE!
|
||||
└─────────────── ──┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### After (Fixed)
|
||||
|
||||
```text
|
||||
┌─────────────── ──┐ ┌─────────────────┐
|
||||
│ Main Thread │ │ Worker Thread │
|
||||
│ │ │ │
|
||||
│ WebPlatformService │───→│registerSQLWorker│
|
||||
│ │ │ ↓ │
|
||||
│ [Send Messages] │ │ databaseService │
|
||||
│ │ │(Single Instance)│
|
||||
│ │ │ ↓ │
|
||||
│ │ │[Run Migrations] │ ← ONCE ONLY!
|
||||
└─────────────── ──┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## New Security Considerations
|
||||
|
||||
### 1. **Message Validation**
|
||||
|
||||
- All worker messages validated for required fields
|
||||
- Unknown message types rejected with errors
|
||||
- Proper error responses prevent information leakage
|
||||
|
||||
### 2. **Timeout Protection**
|
||||
|
||||
- 30-second timeout prevents hung operations
|
||||
- Automatic cleanup of pending messages
|
||||
- Worker health checks via ping/pong
|
||||
|
||||
### 3. **Error Sanitization**
|
||||
|
||||
- Error messages logged but not exposed raw to main thread
|
||||
- Stack traces included only in development
|
||||
- Graceful handling of worker failures
|
||||
|
||||
## Testing Considerations
|
||||
|
||||
### 1. **Unit Tests Needed**
|
||||
|
||||
- Worker message handling
|
||||
- WebPlatformService worker communication
|
||||
- Error handling and timeouts
|
||||
- Migration execution (should run once only)
|
||||
|
||||
### 2. **Integration Tests**
|
||||
|
||||
- End-to-end database operations
|
||||
- Worker lifecycle management
|
||||
- Cross-browser compatibility (especially Safari)
|
||||
|
||||
### 3. **Performance Tests**
|
||||
|
||||
- Message passing overhead
|
||||
- Database operation throughput
|
||||
- Memory usage with worker communication
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
### 1. **Modern Browsers**
|
||||
|
||||
- Chrome/Edge: Full SharedArrayBuffer support
|
||||
- Firefox: Full SharedArrayBuffer support (with headers)
|
||||
- Safari: Uses IndexedDB fallback via `initBackend()`
|
||||
|
||||
### 2. **Required Headers**
|
||||
|
||||
```text
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
```
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
### 1. **Development**
|
||||
|
||||
- Enhanced logging shows worker message flow
|
||||
- Clear separation between worker and main thread logs
|
||||
- Easy debugging via browser DevTools
|
||||
|
||||
### 2. **Production**
|
||||
|
||||
- Reduced logging overhead
|
||||
- Optimized message passing
|
||||
- Proper error reporting without sensitive data
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. **Potential Optimizations**
|
||||
|
||||
- Message batching for bulk operations
|
||||
- Connection pooling simulation
|
||||
- Persistent worker state management
|
||||
|
||||
### 2. **Additional Features**
|
||||
|
||||
- Database backup/restore via worker
|
||||
- Schema introspection commands
|
||||
- Performance monitoring hooks
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise, rollback involves:
|
||||
|
||||
1. Restore original `WebPlatformService.ts`
|
||||
2. Restore original `registerSQLWorker.js`
|
||||
3. Restore original `main.web.ts`
|
||||
4. Remove `worker-messages.ts` interface
|
||||
|
||||
## Commit Messages
|
||||
|
||||
```bash
|
||||
git add src/interfaces/worker-messages.ts
|
||||
git commit -m "Add worker message interface for type-safe database communication
|
||||
|
||||
- Define TypeScript interfaces for worker request/response messages
|
||||
- Include query, exec, getOneRow, init, and ping message types
|
||||
- Provide type safety for web platform worker messaging"
|
||||
|
||||
git add src/registerSQLWorker.js
|
||||
git commit -m "Implement message-based worker for single-point database access
|
||||
|
||||
- Replace simple auto-init with comprehensive message handler
|
||||
- Add support for query, exec, getOneRow, init, ping operations
|
||||
- Implement proper error handling and response management
|
||||
- Ensure single database initialization point to prevent double migrations"
|
||||
|
||||
git add src/services/platforms/WebPlatformService.ts
|
||||
git commit -m "Migrate WebPlatformService to worker-only database access
|
||||
|
||||
- Remove direct databaseService import to prevent dual context issue
|
||||
- Implement worker-based messaging for all database operations
|
||||
- Add worker lifecycle management with initialization tracking
|
||||
- Include message timeout and error handling for reliability
|
||||
- Add Safari compatibility with initBackend call"
|
||||
|
||||
git add src/main.web.ts
|
||||
git commit -m "Remove duplicate worker creation from main.web.ts
|
||||
|
||||
- Worker initialization now handled by WebPlatformService
|
||||
- Prevents duplicate worker creation and database contexts
|
||||
- Simplifies main thread initialization"
|
||||
|
||||
git add WORKER_ONLY_DATABASE_IMPLEMENTATION.md
|
||||
git commit -m "Document worker-only database implementation
|
||||
|
||||
- Comprehensive documentation of architecture changes
|
||||
- Explain problem solved and benefits achieved
|
||||
- Include security considerations and testing requirements"
|
||||
```
|
||||
@@ -1,125 +0,0 @@
|
||||
# Architecture Decisions
|
||||
|
||||
This document records key architectural decisions made during the development of TimeSafari.
|
||||
|
||||
## Platform Service Architecture: Mixins over Composables
|
||||
|
||||
**Date:** July 2, 2025
|
||||
**Status:** Accepted
|
||||
**Context:** Need for consistent platform service access across Vue components
|
||||
|
||||
### Decision
|
||||
|
||||
**Use Vue mixins for platform service access instead of Vue 3 Composition API composables.**
|
||||
|
||||
### Rationale
|
||||
|
||||
#### Why Mixins Were Chosen
|
||||
|
||||
1. **Existing Architecture Consistency**
|
||||
- The entire codebase uses class-based components with `vue-facing-decorator`
|
||||
- All components follow the established pattern of extending Vue class
|
||||
- Mixins integrate seamlessly with the existing architecture
|
||||
|
||||
2. **Performance Benefits**
|
||||
- **Caching Layer**: `PlatformServiceMixin` provides smart TTL-based caching
|
||||
- **Ultra-Concise Methods**: Short methods like `$db()`, `$exec()`, `$one()` reduce boilerplate
|
||||
- **Settings Shortcuts**: `$saveSettings()`, `$saveMySettings()` eliminate 90% of update boilerplate
|
||||
- **Memory Management**: WeakMap-based caching prevents memory leaks
|
||||
|
||||
3. **Developer Experience**
|
||||
- **Familiar Pattern**: Mixins are well-understood by the team
|
||||
- **Type Safety**: Full TypeScript support with proper interfaces
|
||||
- **Error Handling**: Centralized error handling across components
|
||||
- **Code Reduction**: Reduces database code by up to 80%
|
||||
|
||||
4. **Production Readiness**
|
||||
- **Mature Implementation**: `PlatformServiceMixin` is actively used and tested
|
||||
- **Comprehensive Features**: Includes transaction support, cache management, settings shortcuts
|
||||
- **Security**: Proper input validation and error handling
|
||||
|
||||
#### Why Composables Were Rejected
|
||||
|
||||
1. **Architecture Mismatch**
|
||||
- Would require rewriting all components to use Composition API
|
||||
- Breaks consistency with existing class-based component pattern
|
||||
- Requires significant refactoring effort
|
||||
|
||||
2. **Limited Features**
|
||||
- Basic platform service access without caching
|
||||
- No settings management shortcuts
|
||||
- No ultra-concise database methods
|
||||
- Would require additional development to match mixin capabilities
|
||||
|
||||
3. **Performance Considerations**
|
||||
- No built-in caching layer
|
||||
- Would require manual implementation of performance optimizations
|
||||
- More verbose for common operations
|
||||
|
||||
### Implementation
|
||||
|
||||
#### Current Usage
|
||||
|
||||
```typescript
|
||||
// Component implementation
|
||||
@Component({
|
||||
mixins: [PlatformServiceMixin],
|
||||
})
|
||||
export default class HomeView extends Vue {
|
||||
async mounted() {
|
||||
// Ultra-concise cached settings loading
|
||||
const settings = await this.$settings({
|
||||
apiServer: "",
|
||||
activeDid: "",
|
||||
isRegistered: false,
|
||||
});
|
||||
|
||||
// Cached contacts loading
|
||||
this.allContacts = await this.$contacts();
|
||||
|
||||
// Settings update with automatic cache invalidation
|
||||
await this.$saveMySettings({ isRegistered: true });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Key Features
|
||||
|
||||
- **Cached Database Operations**: `$contacts()`, `$settings()`, `$accountSettings()`
|
||||
- **Settings Shortcuts**: `$saveSettings()`, `$saveMySettings()`, `$saveUserSettings()`
|
||||
- **Ultra-Concise Methods**: `$db()`, `$exec()`, `$one()`, `$query()`, `$first()`
|
||||
- **Cache Management**: `$refreshSettings()`, `$clearAllCaches()`
|
||||
- **Transaction Support**: `$withTransaction()` with automatic rollback
|
||||
|
||||
### Consequences
|
||||
|
||||
#### Positive
|
||||
|
||||
- **Consistent Architecture**: All components follow the same pattern
|
||||
- **High Performance**: Smart caching reduces database calls by 80%+
|
||||
- **Developer Productivity**: Ultra-concise methods reduce boilerplate by 90%
|
||||
- **Type Safety**: Full TypeScript support with proper interfaces
|
||||
- **Memory Safety**: WeakMap-based caching prevents memory leaks
|
||||
|
||||
#### Negative
|
||||
|
||||
- **Vue 2 Pattern**: Uses older mixin pattern instead of modern Composition API
|
||||
- **Tight Coupling**: Components are coupled to the mixin implementation
|
||||
- **Testing Complexity**: Mixins can make unit testing more complex
|
||||
|
||||
### Future Considerations
|
||||
|
||||
1. **Migration Path**: If Vue 4 or future versions deprecate mixins, we may need to migrate
|
||||
2. **Performance Monitoring**: Continue monitoring caching performance and adjust TTL values
|
||||
3. **Feature Expansion**: Add new ultra-concise methods as needed
|
||||
4. **Testing Strategy**: Develop comprehensive testing strategies for mixin-based components
|
||||
|
||||
### Related Documentation
|
||||
|
||||
- [PlatformServiceMixin Implementation](../src/utils/PlatformServiceMixin.ts)
|
||||
- [TimeSafari Cross-Platform Architecture Guide](./build-modernization-context.md)
|
||||
- [Database Migration Guide](./database-migration-guide.md)
|
||||
|
||||
---
|
||||
|
||||
*This decision was made based on the current codebase architecture and team expertise. The mixin approach provides the best balance of performance, developer experience, and architectural consistency for the TimeSafari application.*
|
||||
@@ -1,51 +0,0 @@
|
||||
# TimeSafari Build Modernization Context
|
||||
|
||||
**Author:** Matthew Raymer
|
||||
|
||||
## Motivation
|
||||
- Eliminate manual hacks and post-build scripts for Electron builds
|
||||
- Ensure maintainability, reproducibility, and security of build outputs
|
||||
- Unify build, test, and deployment scripts for developer experience and CI/CD
|
||||
|
||||
## Key Technical Decisions
|
||||
- **Vite is the single source of truth for build output**
|
||||
- All Electron build output (main process, preload, renderer HTML/CSS/JS) is managed by `vite.config.electron.mts`
|
||||
- **CSS injection for Electron is handled by a Vite plugin**
|
||||
- No more manual HTML/CSS edits or post-build scripts
|
||||
- **Build scripts are unified and robust**
|
||||
- Use `./scripts/build-electron.sh` for all Electron builds
|
||||
- No more `fix-inject-css.js` or similar hacks
|
||||
- **Output structure is deterministic and ASAR-friendly**
|
||||
- Main process: `dist-electron/main.js`
|
||||
- Preload: `dist-electron/preload.js`
|
||||
- Renderer assets: `dist-electron/www/` (HTML, CSS, JS)
|
||||
|
||||
## Security & Maintenance Checklist
|
||||
- [x] All scripts and configs are committed and documented
|
||||
- [x] No manual file hacks remain
|
||||
- [x] All build output is deterministic and reproducible
|
||||
- [x] No sensitive data is exposed in the build process
|
||||
- [x] Documentation (`BUILDING.md`) is up to date
|
||||
|
||||
## How to Build Electron
|
||||
1. Run:
|
||||
```bash
|
||||
./scripts/build-electron.sh
|
||||
```
|
||||
2. Output will be in `dist-electron/`:
|
||||
- `main.js`, `preload.js` in root
|
||||
- `www/` contains all renderer assets
|
||||
3. No manual post-processing is required
|
||||
|
||||
## Customization
|
||||
- **Vite config:** All build output and asset handling is controlled in `vite.config.electron.mts`
|
||||
- **CSS/HTML injection:** Use Vite plugins (see `electron-css-injection` in the config) for further customization
|
||||
- **Build scripts:** All orchestration is in `scripts/` and documented in `BUILDING.md`
|
||||
|
||||
## For Future Developers
|
||||
- Always use Vite plugins/config for build output changes
|
||||
- Never manually edit built files or inject assets post-build
|
||||
- Keep documentation and scripts in sync with the build process
|
||||
|
||||
---
|
||||
This file documents the context and rationale for the build modernization and should be included in the repository for onboarding and future reference.
|
||||
@@ -1,163 +0,0 @@
|
||||
# Circular Dependency Analysis
|
||||
|
||||
## Overview
|
||||
|
||||
This document analyzes the current state of circular dependencies in the TimeSafari codebase, particularly focusing on the migration from Dexie to SQLite and the PlatformServiceMixin implementation.
|
||||
|
||||
## Current Circular Dependency Status
|
||||
|
||||
### ✅ **EXCELLENT NEWS: All Circular Dependencies RESOLVED**
|
||||
|
||||
The codebase currently has **no active circular dependencies** that are causing runtime or compilation errors. All circular dependency issues have been successfully resolved.
|
||||
|
||||
### 🔍 **Resolved Dependency Patterns**
|
||||
|
||||
#### 1. **Logger → PlatformServiceFactory → Logger** (RESOLVED)
|
||||
- **Status**: ✅ **RESOLVED**
|
||||
- **Previous Issue**: Logger imported `logToDb` from databaseUtil, which imported logger
|
||||
- **Solution**: Logger now uses direct database access via PlatformServiceFactory
|
||||
- **Implementation**: Self-contained `logToDatabase()` function in logger.ts
|
||||
|
||||
#### 2. **PlatformServiceMixin → databaseUtil → logger → PlatformServiceMixin** (RESOLVED)
|
||||
- **Status**: ✅ **RESOLVED**
|
||||
- **Previous Issue**: PlatformServiceMixin imported `memoryLogs` from databaseUtil
|
||||
- **Solution**: Created self-contained `_memoryLogs` array in PlatformServiceMixin
|
||||
- **Implementation**: Self-contained memory logs implementation
|
||||
|
||||
#### 3. **databaseUtil → logger → PlatformServiceFactory → databaseUtil** (RESOLVED)
|
||||
- **Status**: ✅ **RESOLVED**
|
||||
- **Previous Issue**: databaseUtil imported logger, which could create loops
|
||||
- **Solution**: Logger is now self-contained and doesn't import from databaseUtil
|
||||
|
||||
#### 4. **Utility Files → databaseUtil → PlatformServiceMixin** (RESOLVED)
|
||||
- **Status**: ✅ **RESOLVED**
|
||||
- **Previous Issue**: `src/libs/util.ts` and `src/services/deepLinks.ts` imported from databaseUtil
|
||||
- **Solution**: Replaced with self-contained implementations and PlatformServiceFactory usage
|
||||
- **Implementation**:
|
||||
- Self-contained `parseJsonField()` and `mapQueryResultToValues()` functions
|
||||
- Direct PlatformServiceFactory usage for database operations
|
||||
- Console logging instead of databaseUtil logging functions
|
||||
|
||||
## Detailed Dependency Analysis
|
||||
|
||||
### ✅ **All Critical Dependencies Resolved**
|
||||
|
||||
#### PlatformServiceMixin Independence
|
||||
- **Status**: ✅ **COMPLETE**
|
||||
- **Achievement**: PlatformServiceMixin has no external dependencies on databaseUtil
|
||||
- **Implementation**: Self-contained memory logs and utility functions
|
||||
- **Impact**: Enables complete migration of databaseUtil functions to PlatformServiceMixin
|
||||
|
||||
#### Logger Independence
|
||||
- **Status**: ✅ **COMPLETE**
|
||||
- **Achievement**: Logger is completely self-contained
|
||||
- **Implementation**: Direct database access via PlatformServiceFactory
|
||||
- **Impact**: Eliminates all circular dependency risks
|
||||
|
||||
#### Utility Files Independence
|
||||
- **Status**: ✅ **COMPLETE**
|
||||
- **Achievement**: All utility files no longer depend on databaseUtil
|
||||
- **Implementation**: Self-contained functions and direct platform service access
|
||||
- **Impact**: Enables complete databaseUtil migration
|
||||
|
||||
### 🎯 **Migration Readiness Status**
|
||||
|
||||
#### Files Ready for Migration (52 files)
|
||||
1. **Components** (15 files):
|
||||
- `PhotoDialog.vue`
|
||||
- `FeedFilters.vue`
|
||||
- `UserNameDialog.vue`
|
||||
- `ImageMethodDialog.vue`
|
||||
- `OfferDialog.vue`
|
||||
- `OnboardingDialog.vue`
|
||||
- `PushNotificationPermission.vue`
|
||||
- `GiftedPrompts.vue`
|
||||
- `GiftedDialog.vue`
|
||||
- `World/components/objects/landmarks.js`
|
||||
- And 5 more...
|
||||
|
||||
2. **Views** (30+ files):
|
||||
- `IdentitySwitcherView.vue`
|
||||
- `ContactEditView.vue`
|
||||
- `ContactGiftingView.vue`
|
||||
- `ImportAccountView.vue`
|
||||
- `OnboardMeetingMembersView.vue`
|
||||
- `RecentOffersToUserProjectsView.vue`
|
||||
- `ClaimCertificateView.vue`
|
||||
- `NewActivityView.vue`
|
||||
- `HelpView.vue`
|
||||
- `NewEditProjectView.vue`
|
||||
- And 20+ more...
|
||||
|
||||
3. **Services** (5 files):
|
||||
- `deepLinks.ts` ✅ **MIGRATED**
|
||||
- `endorserServer.ts`
|
||||
- `libs/util.ts` ✅ **MIGRATED**
|
||||
- `test/index.ts`
|
||||
|
||||
### 🟢 **Healthy Dependencies**
|
||||
|
||||
#### Logger Usage (80+ files)
|
||||
- **Status**: ✅ **HEALTHY**
|
||||
- **Pattern**: All files import logger from `@/utils/logger`
|
||||
- **Impact**: No circular dependencies, logger is self-contained
|
||||
- **Benefit**: Centralized logging with database integration
|
||||
|
||||
## Resolution Strategy - COMPLETED
|
||||
|
||||
### ✅ **Phase 1: Complete PlatformServiceMixin Independence (COMPLETE)**
|
||||
1. **Removed memoryLogs import** from PlatformServiceMixin ✅
|
||||
2. **Created self-contained memoryLogs** implementation ✅
|
||||
3. **Added missing utility methods** to PlatformServiceMixin ✅
|
||||
|
||||
### ✅ **Phase 2: Utility Files Migration (COMPLETE)**
|
||||
1. **Migrated deepLinks.ts** - Replaced databaseUtil logging with console logging ✅
|
||||
2. **Migrated util.ts** - Replaced databaseUtil functions with self-contained implementations ✅
|
||||
3. **Updated all PlatformServiceFactory calls** to use async pattern ✅
|
||||
|
||||
### 🎯 **Phase 3: File-by-File Migration (READY TO START)**
|
||||
1. **High-usage files first** (views, core components)
|
||||
2. **Replace databaseUtil imports** with PlatformServiceMixin
|
||||
3. **Update function calls** to use mixin methods
|
||||
|
||||
### 🎯 **Phase 4: Cleanup (FUTURE)**
|
||||
1. **Remove unused databaseUtil functions**
|
||||
2. **Update TypeScript interfaces**
|
||||
3. **Remove databaseUtil imports** from all files
|
||||
|
||||
## Current Status Summary
|
||||
|
||||
### ✅ **Resolved Issues**
|
||||
1. **Logger circular dependency** - Fixed with self-contained implementation
|
||||
2. **PlatformServiceMixin circular dependency** - Fixed with self-contained memoryLogs
|
||||
3. **Utility files circular dependency** - Fixed with self-contained implementations
|
||||
4. **TypeScript compilation** - No circular dependency errors
|
||||
5. **Runtime stability** - No circular dependency crashes
|
||||
|
||||
### 🎯 **Ready for Next Phase**
|
||||
1. **52 files** ready for databaseUtil migration
|
||||
2. **PlatformServiceMixin** fully independent and functional
|
||||
3. **Clear migration path** - Well-defined targets and strategy
|
||||
|
||||
## Benefits of Current State
|
||||
|
||||
### ✅ **Achieved**
|
||||
1. **No runtime circular dependencies** - Application runs without crashes
|
||||
2. **Self-contained logger** - No more logger/databaseUtil loops
|
||||
3. **PlatformServiceMixin ready** - All methods implemented and independent
|
||||
4. **Utility files independent** - No more databaseUtil dependencies
|
||||
5. **Clear migration path** - Well-defined targets and strategy
|
||||
|
||||
### 🎯 **Expected After Migration**
|
||||
1. **Complete databaseUtil migration** - Single source of truth
|
||||
2. **Eliminated circular dependencies** - Clean architecture
|
||||
3. **Improved performance** - Caching and optimization
|
||||
4. **Better maintainability** - Centralized database operations
|
||||
|
||||
---
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Created**: 2025-07-05
|
||||
**Status**: ✅ **COMPLETE - All Circular Dependencies Resolved**
|
||||
**Last Updated**: 2025-01-06
|
||||
**Note**: PlatformServiceMixin circular dependency completely resolved. Ready for Phase 2: File-by-File Migration
|
||||
@@ -1,314 +0,0 @@
|
||||
# Component Communication Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This guide establishes our preferred patterns for component communication in Vue.js applications, with a focus on maintainability, type safety, and developer experience.
|
||||
|
||||
## Core Principle: Function Props Over $emit
|
||||
|
||||
**Preference**: Use function props for business logic and data operations, reserve $emit for DOM-like events.
|
||||
|
||||
### Why Function Props?
|
||||
|
||||
1. **Better TypeScript Support**: Full type checking of parameters and return values
|
||||
2. **Superior IDE Navigation**: Ctrl+click takes you directly to implementation
|
||||
3. **Explicit Contracts**: Clear declaration of what functions a component needs
|
||||
4. **Easier Testing**: Simple to mock and test in isolation
|
||||
5. **Flexibility**: Can pass any function, not just event handlers
|
||||
|
||||
### When to Use $emit
|
||||
|
||||
1. **DOM-like Events**: `@click`, `@input`, `@submit`, `@change`
|
||||
2. **Lifecycle Events**: `@mounted`, `@before-unmount`, `@updated`
|
||||
3. **Form Validation**: `@validation-error`, `@validation-success`
|
||||
4. **Event Bubbling**: When events need to bubble through multiple components
|
||||
5. **Vue DevTools Integration**: When you want events visible in DevTools timeline
|
||||
|
||||
## Implementation Patterns
|
||||
|
||||
### Function Props Pattern
|
||||
|
||||
```typescript
|
||||
// Child Component
|
||||
@Component({
|
||||
name: "MyComponent"
|
||||
})
|
||||
export default class MyComponent extends Vue {
|
||||
@Prop({ required: true }) onSave!: (data: SaveData) => Promise<void>;
|
||||
@Prop({ required: true }) onCancel!: () => void;
|
||||
@Prop({ required: false }) onValidate?: (data: FormData) => boolean;
|
||||
|
||||
async handleSave() {
|
||||
const data = this.collectFormData();
|
||||
await this.onSave(data);
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
this.onCancel();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- Parent Template -->
|
||||
<MyComponent
|
||||
:on-save="handleSave"
|
||||
:on-cancel="handleCancel"
|
||||
:on-validate="validateForm"
|
||||
/>
|
||||
```
|
||||
|
||||
### $emit Pattern (for DOM-like events)
|
||||
|
||||
```typescript
|
||||
// Child Component
|
||||
@Component({
|
||||
name: "FormComponent"
|
||||
})
|
||||
export default class FormComponent extends Vue {
|
||||
@Emit("submit")
|
||||
handleSubmit() {
|
||||
return this.formData;
|
||||
}
|
||||
|
||||
@Emit("input")
|
||||
handleInput(value: string) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- Parent Template -->
|
||||
<FormComponent
|
||||
@submit="handleFormSubmit"
|
||||
@input="handleInputChange"
|
||||
/>
|
||||
```
|
||||
|
||||
## Automatic Code Generation Guidelines
|
||||
|
||||
### Component Template Generation
|
||||
|
||||
When generating component templates, follow these patterns:
|
||||
|
||||
#### Function Props Template
|
||||
```vue
|
||||
<template>
|
||||
<div class="component-name">
|
||||
<!-- Component content -->
|
||||
<button @click="handleAction">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-facing-decorator";
|
||||
|
||||
@Component({
|
||||
name: "ComponentName"
|
||||
})
|
||||
export default class ComponentName extends Vue {
|
||||
@Prop({ required: true }) onAction!: () => void;
|
||||
@Prop({ required: true }) buttonText!: string;
|
||||
@Prop({ required: false }) disabled?: boolean;
|
||||
|
||||
handleAction() {
|
||||
if (!this.disabled) {
|
||||
this.onAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### $emit Template (for DOM events)
|
||||
```vue
|
||||
<template>
|
||||
<div class="component-name">
|
||||
<!-- Component content -->
|
||||
<button @click="handleClick">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
|
||||
|
||||
@Component({
|
||||
name: "ComponentName"
|
||||
})
|
||||
export default class ComponentName extends Vue {
|
||||
@Prop({ required: true }) buttonText!: string;
|
||||
@Prop({ required: false }) disabled?: boolean;
|
||||
|
||||
@Emit("click")
|
||||
handleClick() {
|
||||
return { disabled: this.disabled };
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Code Generation Rules
|
||||
|
||||
#### 1. Function Props for Business Logic
|
||||
- **Data operations**: Save, delete, update, validate
|
||||
- **Navigation**: Route changes, modal opening/closing
|
||||
- **State management**: Store actions, state updates
|
||||
- **API calls**: Data fetching, form submissions
|
||||
|
||||
#### 2. $emit for User Interactions
|
||||
- **Click events**: Button clicks, link navigation
|
||||
- **Form events**: Input changes, form submissions
|
||||
- **Lifecycle events**: Component mounting, unmounting
|
||||
- **UI events**: Focus, blur, scroll, resize
|
||||
|
||||
#### 3. Naming Conventions
|
||||
|
||||
**Function Props:**
|
||||
```typescript
|
||||
// Action-oriented names
|
||||
onSave: (data: SaveData) => Promise<void>
|
||||
onDelete: (id: string) => Promise<void>
|
||||
onUpdate: (item: Item) => void
|
||||
onValidate: (data: FormData) => boolean
|
||||
onNavigate: (route: string) => void
|
||||
```
|
||||
|
||||
**$emit Events:**
|
||||
```typescript
|
||||
// Event-oriented names
|
||||
@click: (event: MouseEvent) => void
|
||||
@input: (value: string) => void
|
||||
@submit: (data: FormData) => void
|
||||
@focus: (event: FocusEvent) => void
|
||||
@mounted: () => void
|
||||
```
|
||||
|
||||
### TypeScript Integration
|
||||
|
||||
#### Function Prop Types
|
||||
```typescript
|
||||
// Define reusable function types
|
||||
interface SaveHandler {
|
||||
(data: SaveData): Promise<void>;
|
||||
}
|
||||
|
||||
interface ValidationHandler {
|
||||
(data: FormData): boolean;
|
||||
}
|
||||
|
||||
// Use in components
|
||||
@Prop({ required: true }) onSave!: SaveHandler;
|
||||
@Prop({ required: true }) onValidate!: ValidationHandler;
|
||||
```
|
||||
|
||||
#### Event Types
|
||||
```typescript
|
||||
// Define event payload types
|
||||
interface ClickEvent {
|
||||
target: HTMLElement;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
@Emit("click")
|
||||
handleClick(): ClickEvent {
|
||||
return {
|
||||
target: this.$el,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
### Function Props Testing
|
||||
```typescript
|
||||
// Easy to mock and test
|
||||
const mockOnSave = jest.fn();
|
||||
const wrapper = mount(MyComponent, {
|
||||
propsData: {
|
||||
onSave: mockOnSave
|
||||
}
|
||||
});
|
||||
|
||||
await wrapper.vm.handleSave();
|
||||
expect(mockOnSave).toHaveBeenCalledWith(expectedData);
|
||||
```
|
||||
|
||||
### $emit Testing
|
||||
```typescript
|
||||
// Requires event simulation
|
||||
const wrapper = mount(MyComponent);
|
||||
await wrapper.find('button').trigger('click');
|
||||
expect(wrapper.emitted('click')).toBeTruthy();
|
||||
```
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### From $emit to Function Props
|
||||
|
||||
1. **Identify business logic events** (not DOM events)
|
||||
2. **Add function props** to component interface
|
||||
3. **Update parent components** to pass functions
|
||||
4. **Remove $emit decorators** and event handlers
|
||||
5. **Update tests** to use function mocks
|
||||
|
||||
### Example Migration
|
||||
|
||||
**Before ($emit):**
|
||||
```typescript
|
||||
@Emit("save")
|
||||
handleSave() {
|
||||
return this.formData;
|
||||
}
|
||||
```
|
||||
|
||||
**After (Function Props):**
|
||||
```typescript
|
||||
@Prop({ required: true }) onSave!: (data: FormData) => void;
|
||||
|
||||
handleSave() {
|
||||
this.onSave(this.formData);
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Use function props** for business logic, data operations, and complex interactions
|
||||
2. **Use $emit** for DOM-like events, lifecycle events, and simple user interactions
|
||||
3. **Be consistent** within your codebase
|
||||
4. **Document your patterns** for team alignment
|
||||
5. **Consider TypeScript** when choosing between approaches
|
||||
6. **Test both patterns** appropriately
|
||||
|
||||
## Code Generation Templates
|
||||
|
||||
### Component Generator Input
|
||||
```typescript
|
||||
interface ComponentSpec {
|
||||
name: string;
|
||||
props: Array<{
|
||||
name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
isFunction: boolean;
|
||||
}>;
|
||||
events: Array<{
|
||||
name: string;
|
||||
payloadType?: string;
|
||||
}>;
|
||||
template: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Generated Output
|
||||
```typescript
|
||||
// Generator should automatically choose function props vs $emit
|
||||
// based on the nature of the interaction (business logic vs DOM event)
|
||||
```
|
||||
|
||||
This guide ensures consistent, maintainable component communication patterns across the application.
|
||||
@@ -1,116 +0,0 @@
|
||||
# CORS Disabled for Universal Image Support
|
||||
|
||||
## Decision Summary
|
||||
|
||||
CORS headers have been **disabled** to support Time Safari's core mission: enabling users to share images from any domain without restrictions.
|
||||
|
||||
## What Changed
|
||||
|
||||
### ❌ Removed CORS Headers
|
||||
- `Cross-Origin-Opener-Policy: same-origin`
|
||||
- `Cross-Origin-Embedder-Policy: require-corp`
|
||||
|
||||
### ✅ Results
|
||||
- Images from **any domain** now work in development and production
|
||||
- No proxy configuration needed
|
||||
- No whitelist of supported image hosts
|
||||
- True community-driven image sharing
|
||||
|
||||
## Technical Tradeoffs
|
||||
|
||||
### 🔻 Lost: SharedArrayBuffer Performance
|
||||
- **Before**: Fast SQLite operations via SharedArrayBuffer
|
||||
- **After**: Slightly slower IndexedDB fallback mode
|
||||
- **Impact**: Minimal for typical usage - absurd-sql automatically falls back
|
||||
|
||||
### 🔺 Gained: Universal Image Support
|
||||
- **Before**: Only specific domains worked (TimeSafari, Flickr, Imgur, etc.)
|
||||
- **After**: Any image URL works immediately
|
||||
- **Impact**: Massive improvement for user experience
|
||||
|
||||
## Architecture Impact
|
||||
|
||||
### Database Operations
|
||||
```typescript
|
||||
// absurd-sql automatically detects SharedArrayBuffer availability
|
||||
if (typeof SharedArrayBuffer === "undefined") {
|
||||
// Uses IndexedDB backend (current state)
|
||||
console.log("Using IndexedDB fallback mode");
|
||||
} else {
|
||||
// Uses SharedArrayBuffer (not available due to disabled CORS)
|
||||
console.log("Using SharedArrayBuffer mode");
|
||||
}
|
||||
```
|
||||
|
||||
### Image Loading
|
||||
```typescript
|
||||
// All images load directly now
|
||||
export function transformImageUrlForCors(imageUrl: string): string {
|
||||
return imageUrl; // No transformation needed
|
||||
}
|
||||
```
|
||||
|
||||
## Why This Was The Right Choice
|
||||
|
||||
### Time Safari's Use Case
|
||||
- **Community platform** where users share content from anywhere
|
||||
- **User-generated content** includes images from arbitrary websites
|
||||
- **Flexibility** is more important than marginal performance gains
|
||||
|
||||
### Alternative Would Require
|
||||
- Pre-configuring proxies for every possible image hosting service
|
||||
- Constantly updating proxy list as users find new sources
|
||||
- Poor user experience when images fail to load
|
||||
- Impossible to support the "any domain" requirement
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
### Database Operations
|
||||
- **SharedArrayBuffer**: ~2x faster for large operations
|
||||
- **IndexedDB**: Still very fast for typical Time Safari usage
|
||||
- **Real Impact**: Negligible for typical user operations
|
||||
|
||||
### Image Loading
|
||||
- **With CORS**: Many images failed to load in development
|
||||
- **Without CORS**: All images load immediately
|
||||
- **Real Impact**: Massive improvement in user experience
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
| Browser | SharedArrayBuffer | IndexedDB | Image Loading |
|
||||
|---------|------------------|-----------|---------------|
|
||||
| Chrome | ❌ (CORS disabled) | ✅ Works | ✅ Any domain |
|
||||
| Firefox | ❌ (CORS disabled) | ✅ Works | ✅ Any domain |
|
||||
| Safari | ❌ (CORS disabled) | ✅ Works | ✅ Any domain |
|
||||
| Edge | ❌ (CORS disabled) | ✅ Works | ✅ Any domain |
|
||||
|
||||
## Migration Notes
|
||||
|
||||
### For Developers
|
||||
- No code changes needed
|
||||
- `transformImageUrlForCors()` still exists but returns original URL
|
||||
- All existing image references work without modification
|
||||
|
||||
### For Users
|
||||
- Images from any website now work immediately
|
||||
- No more "image failed to load" issues in development
|
||||
- Consistent behavior between development and production
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### If Performance Becomes Critical
|
||||
1. **Selective CORS**: Enable only for specific operations
|
||||
2. **Service Worker**: Handle image proxying at service worker level
|
||||
3. **Build-time Processing**: Pre-process images during build
|
||||
4. **User Education**: Guide users toward optimized image hosting
|
||||
|
||||
### Monitoring
|
||||
- Track database operation performance
|
||||
- Monitor for any user-reported slowness
|
||||
- Consider re-enabling SharedArrayBuffer if usage patterns change
|
||||
|
||||
## Conclusion
|
||||
|
||||
This change prioritizes **user experience** and **community functionality** over marginal performance gains. The database still works efficiently via IndexedDB, while images now work universally without configuration.
|
||||
|
||||
For a community platform like Time Safari, the ability to share images from any domain is fundamental to the user experience and mission.
|
||||