forked from trent_larson/crowd-funder-for-time-pwa
WIP: add Electron platform configuration to Capacitor
- Add electron platform section to capacitor.config.json - Configure deep linking with timesafari:// scheme - Set up build options for macOS, Windows, and Linux - Configure output directory and file inclusion - Add platform-specific build targets (DMG, NSIS, AppImage) - Support both x64 and arm64 architectures for macOS - Set appropriate app categories for each platform This enables building TimeSafari as a native desktop application using Capacitor's Electron platform while maintaining existing mobile and web functionality.
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
VM4 sandbox_bundle:2 Unable to load preload script: /home/noone/projects/timesafari/crowd-master/preload.js
|
||||
(anonymous) @ VM4 sandbox_bundle:2
|
||||
VM4 sandbox_bundle:2 Error: ENOENT: no such file or directory, open '/home/noone/projects/timesafari/crowd-master/preload.js'
|
||||
at async open (node:internal/fs/promises:639:25)
|
||||
at async Object.readFile (node:internal/fs/promises:1246:14)
|
||||
at async node:electron/js2c/browser_init:2:108714
|
||||
at async Promise.all (index 0)
|
||||
at async node:electron/js2c/browser_init:2:108650
|
||||
at async IpcMainImpl.<anonymous> (node:electron/js2c/browser_init:2:105615)
|
||||
(anonymous) @ VM4 sandbox_bundle:2
|
||||
main.electron.ts:1
|
||||
|
||||
|
||||
Failed to load resource: net::ERR_FILE_NOT_FOUND
|
||||
index.html:1 Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: file:///tmp/.mount_TimeSaGVOt4a/resources/app.asar/dist-electron/www/src/main.electron.ts
|
||||
@@ -1,4 +0,0 @@
|
||||
main.electron.js:1
|
||||
|
||||
|
||||
Failed to load resource: net::ERR_FILE_NOT_FOUND
|
||||
216
BUILDING.md
216
BUILDING.md
@@ -9,7 +9,8 @@ For a quick dev environment setup, use [pkgx](https://pkgx.dev).
|
||||
- Node.js (LTS version recommended)
|
||||
- npm (comes with Node.js)
|
||||
- Git
|
||||
- For desktop builds: Additional build tools based on your OS
|
||||
- For mobile builds: Android Studio (Android) or Xcode (iOS)
|
||||
- For desktop builds: Capacitor Electron platform
|
||||
|
||||
## Unified Build Scripts
|
||||
|
||||
@@ -28,8 +29,6 @@ TimeSafari now uses unified build scripts that automatically handle environment
|
||||
|
||||
| Script | Purpose | Command |
|
||||
|--------|---------|---------|
|
||||
| `electron-dev.sh` | Electron development | `./scripts/electron-dev.sh` |
|
||||
| `electron-build.sh` | Electron build | `./scripts/build-electron.sh` |
|
||||
| `capacitor-dev.sh` | Capacitor development | `./scripts/capacitor-dev.sh` |
|
||||
| `capacitor-build.sh` | Capacitor build | `./scripts/build-capacitor.sh` |
|
||||
| `web-dev.sh` | Web development | `./scripts/web-dev.sh` |
|
||||
@@ -41,25 +40,22 @@ All scripts automatically set the correct environment variables for their build
|
||||
|
||||
| Build Type | VITE_PLATFORM | VITE_PWA_ENABLED | VITE_DISABLE_PWA | NODE_ENV |
|
||||
|------------|---------------|------------------|------------------|----------|
|
||||
| `electron` | electron | false | true | production* |
|
||||
| `capacitor` | capacitor | false | true | - |
|
||||
| `web` | web | true | false | - |
|
||||
|
||||
*NODE_ENV=production only set when production mode is enabled
|
||||
|
||||
### CLI Options
|
||||
|
||||
All scripts support these options:
|
||||
|
||||
```bash
|
||||
# Show help
|
||||
./scripts/build-electron.sh --help
|
||||
./scripts/build-capacitor.sh --help
|
||||
|
||||
# Enable verbose logging
|
||||
./scripts/build-electron.sh --verbose
|
||||
./scripts/build-capacitor.sh --verbose
|
||||
|
||||
# Show environment variables
|
||||
./scripts/build-electron.sh --env
|
||||
./scripts/build-capacitor.sh --env
|
||||
```
|
||||
|
||||
## Forks
|
||||
@@ -102,12 +98,14 @@ npx jsr add @nostr/tools
|
||||
**Reason**: Resolved Vite/Rollup build issues with deep imports
|
||||
|
||||
**Before** (npm):
|
||||
|
||||
```typescript
|
||||
import { finalizeEvent } from "nostr-tools/lib/cjs/index.js";
|
||||
import { accountFromExtendedKey } from "nostr-tools/lib/cjs/nip06.js";
|
||||
```
|
||||
|
||||
**After** (JSR):
|
||||
|
||||
```typescript
|
||||
import { finalizeEvent } from "@nostr/tools";
|
||||
import { accountFromExtendedKey } from "@nostr/tools/nip06";
|
||||
@@ -210,102 +208,92 @@ VITE_DEFAULT_PUSH_SERVER=https://timesafari.app
|
||||
VITE_PASSKEYS_ENABLED=true
|
||||
```
|
||||
|
||||
## Desktop Build (Electron)
|
||||
## Desktop Build (Capacitor Electron)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install Capacitor CLI:
|
||||
|
||||
```bash
|
||||
npm install -g @capacitor/cli
|
||||
```
|
||||
|
||||
2. Add Electron platform:
|
||||
|
||||
```bash
|
||||
npx cap add electron
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
For development with automatic environment setup:
|
||||
|
||||
```bash
|
||||
./scripts/electron-dev.sh
|
||||
# Build web assets
|
||||
npm run build:capacitor
|
||||
|
||||
# Sync with Capacitor
|
||||
npx cap sync electron
|
||||
|
||||
# Open in Electron
|
||||
npx cap open electron
|
||||
```
|
||||
|
||||
### Production Build
|
||||
|
||||
For production builds with automatic environment setup:
|
||||
For production builds:
|
||||
|
||||
```bash
|
||||
./scripts/build-electron.sh
|
||||
# Build web assets
|
||||
npm run build:capacitor
|
||||
|
||||
# Sync with Capacitor
|
||||
npx cap sync electron
|
||||
|
||||
# Build Electron app
|
||||
npx cap build electron
|
||||
```
|
||||
|
||||
### Linux Packaging
|
||||
### Packaging
|
||||
|
||||
```bash
|
||||
# Build AppImage (recommended)
|
||||
./scripts/build-electron-linux.sh
|
||||
Capacitor Electron uses electron-builder for packaging. Configure the build in `capacitor.config.json`:
|
||||
|
||||
# Build .deb package
|
||||
./scripts/build-electron-linux.sh deb
|
||||
|
||||
# Build production AppImage
|
||||
./scripts/build-electron-linux.sh prod
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"ElectronBuilder": {
|
||||
"buildOptions": {
|
||||
"appId": "app.timesafari.app",
|
||||
"productName": "TimeSafari",
|
||||
"directories": {
|
||||
"output": "dist-electron-packages"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"electron/**/*"
|
||||
],
|
||||
"linux": {
|
||||
"target": ["AppImage", "deb"],
|
||||
"category": "Office"
|
||||
},
|
||||
"mac": {
|
||||
"target": ["dmg", "zip"],
|
||||
"category": "public.app-category.productivity"
|
||||
},
|
||||
"win": {
|
||||
"target": ["nsis", "portable"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The packaged applications will be in `dist-electron-packages/`:
|
||||
- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage`
|
||||
- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb`
|
||||
|
||||
### macOS Packaging
|
||||
|
||||
```bash
|
||||
# Build standard Mac package
|
||||
./scripts/build-electron-mac.sh
|
||||
|
||||
# Build universal package (Intel + Apple Silicon)
|
||||
./scripts/build-electron-mac.sh universal
|
||||
```
|
||||
|
||||
The packaged applications will be in `dist-electron-packages/`:
|
||||
- `.app` bundle: `TimeSafari.app`
|
||||
- `.dmg` installer: `TimeSafari-x.x.x.dmg`
|
||||
- `.zip` archive: `TimeSafari-x.x.x-mac.zip`
|
||||
|
||||
### Code Signing and Notarization (macOS)
|
||||
|
||||
For public distribution on macOS, you need to code sign and notarize your app:
|
||||
|
||||
1. Set up environment variables in `.env` file:
|
||||
```bash
|
||||
CSC_LINK=/path/to/your/certificate.p12
|
||||
CSC_KEY_PASSWORD=your_certificate_password
|
||||
APPLE_ID=your_apple_id
|
||||
APPLE_ID_PASSWORD=your_app_specific_password
|
||||
```
|
||||
|
||||
2. Build with signing:
|
||||
```bash
|
||||
./scripts/build-electron-mac.sh
|
||||
```
|
||||
|
||||
### Running the Packaged App
|
||||
|
||||
- **Linux**:
|
||||
- AppImage: Make executable and run
|
||||
```bash
|
||||
chmod +x dist-electron-packages/TimeSafari-*.AppImage
|
||||
./dist-electron-packages/TimeSafari-*.AppImage
|
||||
```
|
||||
- DEB: Install and run
|
||||
```bash
|
||||
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
|
||||
timesafari
|
||||
```
|
||||
|
||||
- **macOS**:
|
||||
- `.app` bundle: Double-click `TimeSafari.app` in Finder
|
||||
- `.dmg` installer:
|
||||
1. Double-click the `.dmg` file
|
||||
2. Drag the app to your Applications folder
|
||||
3. Launch from Applications
|
||||
- `.zip` archive:
|
||||
1. Extract the `.zip` file
|
||||
2. Move `TimeSafari.app` to your Applications folder
|
||||
3. Launch from Applications
|
||||
|
||||
Note: If you get a security warning when running the app:
|
||||
1. Right-click the app
|
||||
2. Select "Open"
|
||||
3. Click "Open" in the security dialog
|
||||
- **Linux**: AppImage files are self-contained executables
|
||||
- **macOS**: `.app` bundles can be dragged to Applications folder
|
||||
- **Windows**: `.exe` installers or portable executables
|
||||
|
||||
## Mobile Builds (Capacitor)
|
||||
|
||||
@@ -601,12 +589,12 @@ For iOS deep links, configure the URL scheme in Xcode:
|
||||
### Common Issues
|
||||
|
||||
1. **Environment Variables Not Set**
|
||||
- Use `--env` flag to check current environment: `./scripts/build-electron.sh --env`
|
||||
- Use `--env` flag to check current environment: `./scripts/build-capacitor.sh --env`
|
||||
- Verify `.env` file exists and is properly formatted
|
||||
- Check script output for environment setup messages
|
||||
|
||||
2. **Build Failures**
|
||||
- Use `--verbose` flag for detailed logging: `./scripts/build-electron.sh --verbose`
|
||||
- Use `--verbose` flag for detailed logging: `./scripts/build-capacitor.sh --verbose`
|
||||
- Check prerequisites are installed
|
||||
- Verify all dependencies are installed: `npm install`
|
||||
|
||||
@@ -615,14 +603,13 @@ For iOS deep links, configure the URL scheme in Xcode:
|
||||
- Check file permissions on build directories
|
||||
|
||||
4. **Platform-Specific Issues**
|
||||
- **Linux**: Ensure AppImage dependencies are installed
|
||||
- **macOS**: Check code signing certificates and entitlements
|
||||
- **Android**: Verify Android Studio and SDK are properly configured
|
||||
- **iOS**: Ensure Xcode and certificates are set up correctly
|
||||
- **Electron**: Check Capacitor Electron platform installation
|
||||
|
||||
### Getting Help
|
||||
|
||||
- Check script help: `./scripts/build-electron.sh --help`
|
||||
- Check script help: `./scripts/build-capacitor.sh --help`
|
||||
- Review script documentation in `scripts/README.md`
|
||||
- Test environment setup: `./scripts/test-env.sh`
|
||||
- Test common utilities: `./scripts/test-common.sh`
|
||||
@@ -633,20 +620,47 @@ For iOS deep links, configure the URL scheme in Xcode:
|
||||
|----------|------|-------------|-----------------|-------|
|
||||
| `web` | web | true | false | Standard web browser |
|
||||
| `capacitor` | capacitor | false | true | Mobile app (iOS/Android) |
|
||||
| `electron` | electron | false | true | Desktop app (Windows/macOS/Linux) |
|
||||
| `electron` | capacitor | false | true | Desktop app (via Capacitor Electron) |
|
||||
|
||||
## Electron Build: CSS Injection
|
||||
## Platform Service Architecture
|
||||
|
||||
The Electron build now uses Vite's built-in CSS handling with a custom plugin (`electron-css-injection`) that automatically injects CSS links into the generated `index.html` file. This replaces the previous manual CSS injection script.
|
||||
TimeSafari uses a unified platform service architecture that works across all platforms:
|
||||
|
||||
**Plugin:** `vite.config.electron.mts` - `electron-css-injection` plugin
|
||||
### Platform Detection
|
||||
|
||||
**Features:**
|
||||
- Automatically detects and injects CSS files generated by Vite
|
||||
- Ensures proper relative paths for Electron builds
|
||||
- Handles multiple CSS files if present
|
||||
- Provides detailed logging during build process
|
||||
The `CapacitorPlatformService` automatically detects the platform and adjusts capabilities:
|
||||
|
||||
**No manual intervention required** - CSS injection is handled automatically during the Vite build process.
|
||||
```typescript
|
||||
getCapabilities(): PlatformCapabilities {
|
||||
const platform = Capacitor.getPlatform();
|
||||
const isElectron = platform === "electron";
|
||||
|
||||
return {
|
||||
hasFileSystem: true,
|
||||
hasCamera: true,
|
||||
isMobile: !isElectron, // false for Electron, true for mobile
|
||||
isIOS: platform === "ios",
|
||||
hasFileDownload: isElectron, // Electron can download files directly
|
||||
needsFileHandlingInstructions: !isElectron, // Mobile needs instructions
|
||||
isNativeApp: true,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Author:** Matthew Raymer
|
||||
### Unified Database Layer
|
||||
|
||||
All platforms use the same SQLite database through Capacitor plugins:
|
||||
|
||||
- **Mobile**: `@capacitor-community/sqlite` plugin
|
||||
- **Desktop**: Same plugin via Capacitor Electron
|
||||
- **Web**: IndexedDB fallback with absurd-sql
|
||||
|
||||
### Feature Parity
|
||||
|
||||
The same Capacitor plugins work across all platforms:
|
||||
|
||||
- File system operations
|
||||
- Camera access
|
||||
- SQLite database
|
||||
- Deep linking
|
||||
- Sharing functionality
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"appId": "app.timesafari",
|
||||
"appName": "TimeSafari",
|
||||
"webDir": "dist",
|
||||
"bundledWebRuntime": false,
|
||||
"server": {
|
||||
"cleartext": true
|
||||
},
|
||||
@@ -52,5 +51,47 @@
|
||||
"*.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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
case 'capacitor':
|
||||
import('./src/main.capacitor.ts');
|
||||
break;
|
||||
case 'electron':
|
||||
import('./src/main.electron.ts');
|
||||
break;
|
||||
case 'web':
|
||||
default:
|
||||
import('./src/main.web.ts');
|
||||
|
||||
447
package-lock.json
generated
447
package-lock.json
generated
@@ -7549,24 +7549,33 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.4.1.tgz",
|
||||
"integrity": "sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==",
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz",
|
||||
"integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.8.0"
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
@@ -7586,12 +7595,12 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
@@ -7650,102 +7659,6 @@
|
||||
"nostr-wasm": "0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@noble/ciphers": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@noble/hashes": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@scure/base": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@scure/bip32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.1.0",
|
||||
"@noble/hashes": "~1.3.1",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nostr/tools/node_modules/@scure/bip39": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/fs": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
||||
@@ -8724,95 +8637,56 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/base": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz",
|
||||
"integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.4.0",
|
||||
"@noble/hashes": "~1.4.0",
|
||||
"@scure/base": "~1.1.6"
|
||||
"@noble/curves": "~1.1.0",
|
||||
"@noble/hashes": "~1.3.1",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz",
|
||||
"integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.4.0"
|
||||
"@noble/hashes": "1.3.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@scure/base": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
|
||||
"integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz",
|
||||
"integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.4.0",
|
||||
"@scure/base": "~1.1.6"
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39/node_modules/@noble/hashes": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39/node_modules/@scure/base": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
|
||||
"integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@simplewebauthn/browser": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-10.0.0.tgz",
|
||||
@@ -9704,9 +9578,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/leaflet": {
|
||||
"version": "1.9.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.18.tgz",
|
||||
"integrity": "sha512-ht2vsoPjezor5Pmzi5hdsA7F++v5UGq9OlUduWHmMZiuQGIpJ2WS5+Gg9HaAA79gNh1AIPtCqhzejcIZ3lPzXQ==",
|
||||
"version": "1.9.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.19.tgz",
|
||||
"integrity": "sha512-pB+n2daHcZPF2FDaWa+6B0a0mSDf4dPU35y5iTXsx7x/PzzshiX5atYiS1jlBn43X7XvM8AP+AB26lnSk0J4GA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10409,6 +10283,15 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@veramo/did-provider-peer/node_modules/@scure/base": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@veramo/did-provider-peer/node_modules/@veramo/core-types": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@veramo/core-types/-/core-types-6.0.0.tgz",
|
||||
@@ -11834,7 +11717,6 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"possible-typed-array-names": "^1.0.0"
|
||||
@@ -12602,9 +12484,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.25.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
||||
"integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==",
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -12622,8 +12504,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001718",
|
||||
"electron-to-chromium": "^1.5.160",
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
@@ -12961,7 +12843,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
@@ -12993,7 +12874,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
@@ -13103,9 +12983,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001723",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
|
||||
"integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
|
||||
"version": "1.0.30001726",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz",
|
||||
"integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -14860,7 +14740,6 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
@@ -15091,6 +14970,24 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/did-jwt/node_modules/@noble/ciphers": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.4.1.tgz",
|
||||
"integrity": "sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/did-jwt/node_modules/@scure/base": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/did-jwt/node_modules/multiformats": {
|
||||
"version": "9.9.0",
|
||||
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
|
||||
@@ -15582,9 +15479,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.170",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
||||
"integrity": "sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==",
|
||||
"version": "1.5.173",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.173.tgz",
|
||||
"integrity": "sha512-2bFhXP2zqSfQHugjqJIDFVwa+qIxyNApenmXTp9EjaKtdPrES5Qcn9/aSFy/NaP2E+fWG/zxKu/LBvY36p5VNQ==",
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -16016,9 +15913,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.0.tgz",
|
||||
"integrity": "sha512-8qsOYwkkGrahrgoUv76NZi23koqXOGiiEzXMrT8Q7VcYaUISR+5MorIUxfWqYXN0fN/31WbSrxCxFkVQ43wwrA==",
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz",
|
||||
"integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -16238,6 +16135,18 @@
|
||||
"@noble/hashes": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereum-cryptography": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz",
|
||||
@@ -16274,6 +16183,42 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereum-cryptography/node_modules/@scure/base": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
|
||||
"integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereum-cryptography/node_modules/@scure/bip32": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz",
|
||||
"integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.4.0",
|
||||
"@noble/hashes": "~1.4.0",
|
||||
"@scure/base": "~1.1.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereum-cryptography/node_modules/@scure/bip39": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz",
|
||||
"integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.4.0",
|
||||
"@scure/base": "~1.1.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethereumjs-util": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
|
||||
@@ -16341,18 +16286,6 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ethers/node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethers/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
@@ -16446,6 +16379,15 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethr-did/node_modules/@scure/base": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/ethr-did/node_modules/did-jwt": {
|
||||
"version": "8.0.17",
|
||||
"resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-8.0.17.tgz",
|
||||
@@ -17222,7 +17164,6 @@
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-callable": "^1.2.7"
|
||||
@@ -18102,7 +18043,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
@@ -18705,7 +18645,6 @@
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -19149,7 +19088,6 @@
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"which-typed-array": "^1.1.16"
|
||||
@@ -19236,7 +19174,6 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
@@ -23979,21 +23916,53 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
|
||||
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz",
|
||||
"integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"create-hash": "^1.1.2",
|
||||
"create-hmac": "^1.1.4",
|
||||
"ripemd160": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1",
|
||||
"sha.js": "^2.4.8"
|
||||
"create-hash": "~1.1.3",
|
||||
"create-hmac": "^1.1.7",
|
||||
"ripemd160": "=2.0.1",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"sha.js": "^2.4.11",
|
||||
"to-buffer": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/create-hash": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
|
||||
"integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
"ripemd160": "^2.0.0",
|
||||
"sha.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/hash-base": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
|
||||
"integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/ripemd160": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
|
||||
"integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hash-base": "^2.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pe-library": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz",
|
||||
@@ -24163,7 +24132,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -24373,9 +24341,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.1.tgz",
|
||||
"integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -24535,9 +24503,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/protons-runtime": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.5.0.tgz",
|
||||
"integrity": "sha512-EsALjF9QsrEk6gbCx3lmfHxVN0ah7nG3cY7GySD4xf4g8cr7g543zB88Foh897Sr1RQJ9yDCUsoT1i1H/cVUFA==",
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.6.0.tgz",
|
||||
"integrity": "sha512-/Kde+sB9DsMFrddJT/UZWe6XqvL7SL5dbag/DBCElFKhkwDj7XKt53S+mzLyaDP5OqS0wXjV5SA572uWDaT0Hg==",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"dependencies": {
|
||||
"uint8-varint": "^2.0.2",
|
||||
@@ -24744,9 +24712,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode-generator": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.0.tgz",
|
||||
"integrity": "sha512-sqo7otiDq5rA4djRkFI7IjLQqxRrLpIou0d3rqr03JJLUGf5raPh91xCio+lFFbQf0SlcVckStz0EmDEX3EeZA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.1.tgz",
|
||||
"integrity": "sha512-u0zerMlMtKBgkDlDzWXG/7F7jo2En1d7bivC7V33HGqP62XxGiHGnbCJNkSCMxGfNlhXGBGEIamNMHbZ4P4mLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qrcode-terminal": {
|
||||
@@ -26906,7 +26874,6 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
@@ -28836,6 +28803,20 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/to-buffer": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
|
||||
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"isarray": "^2.0.5",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"typed-array-buffer": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -29080,7 +29061,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
|
||||
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.3",
|
||||
@@ -29169,9 +29149,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typeorm": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.24.tgz",
|
||||
"integrity": "sha512-4IrHG7A0tY8l5gEGXfW56VOMfUVWEkWlH/h5wmcyZ+V8oCiLj7iTPp0lEjMEZVrxEkGSdP9ErgTKHKXQApl/oA==",
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz",
|
||||
"integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sqltools/formatter": "^1.2.5",
|
||||
@@ -30352,7 +30332,6 @@
|
||||
"version": "1.1.19",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.7",
|
||||
|
||||
70
package.json
70
package.json
@@ -20,25 +20,14 @@
|
||||
"test:ios": "node scripts/test-ios.js",
|
||||
"check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)",
|
||||
"check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)",
|
||||
"clean:electron": "rimraf dist-electron",
|
||||
"build:electron": "./scripts/build-electron.sh && npm run build:electron-renderer",
|
||||
"build:electron-renderer": "vite build --config vite.config.electron.renderer.mts",
|
||||
"build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||
"build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||
"build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts",
|
||||
"electron:dev": "./scripts/electron-dev.sh",
|
||||
"electron:start": "electron .",
|
||||
"clean:android": "adb uninstall app.timesafari.app || true",
|
||||
"build:android": "./scripts/build-android.sh",
|
||||
"electron:build-linux": "./scripts/build-electron-linux.sh",
|
||||
"electron:build-linux-deb": "./scripts/build-electron-linux.sh deb",
|
||||
"electron:build-linux-prod": "./scripts/build-electron-linux.sh prod",
|
||||
"build:electron-prod": "NODE_ENV=production npm run build:electron",
|
||||
"fastlane:ios:beta": "cd ios && fastlane beta",
|
||||
"fastlane:ios:release": "cd ios && fastlane release",
|
||||
"fastlane:android:beta": "cd android && fastlane beta",
|
||||
"fastlane:android:release": "cd android && fastlane release",
|
||||
"electron:build-mac": "./scripts/build-electron-mac.sh",
|
||||
"electron:build-mac-universal": "./scripts/build-electron-mac.sh universal"
|
||||
"fastlane:android:release": "cd android && fastlane release"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor-community/sqlite": "6.0.2",
|
||||
@@ -145,8 +134,6 @@
|
||||
"browserify-fs": "^1.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"crypto-browserify": "^3.12.1",
|
||||
"electron": "^33.2.1",
|
||||
"electron-builder": "^25.1.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
@@ -163,58 +150,5 @@
|
||||
"typescript": "~5.2.2",
|
||||
"vite": "^5.2.0",
|
||||
"vite-plugin-pwa": "^1.0.0"
|
||||
},
|
||||
"main": "./dist-electron/main.js",
|
||||
"build": {
|
||||
"appId": "app.timesafari.app",
|
||||
"productName": "TimeSafari",
|
||||
"directories": {
|
||||
"output": "dist-electron-packages"
|
||||
},
|
||||
"files": [
|
||||
"dist-electron/**/*",
|
||||
"dist/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "dist-electron/www",
|
||||
"to": "www"
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"target": [
|
||||
"AppImage",
|
||||
"deb"
|
||||
],
|
||||
"category": "Office",
|
||||
"icon": "build/icon.png"
|
||||
},
|
||||
"asar": true,
|
||||
"mac": {
|
||||
"target": [
|
||||
"dmg",
|
||||
"zip"
|
||||
],
|
||||
"category": "public.app-category.productivity",
|
||||
"icon": "build/icon.png",
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "ios/App/App/entitlements.mac.plist",
|
||||
"entitlementsInherit": "ios/App/App/entitlements.mac.plist"
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ All scripts automatically handle environment variables for different build types
|
||||
|----------|------|-------------|-----------------|--------------|
|
||||
| `web` | web | true | false | `build-web.sh` |
|
||||
| `capacitor` | capacitor | false | true | `build-capacitor.sh` |
|
||||
| `electron` | electron | false | true | `build-electron.sh` |
|
||||
|
||||
#### Automatic Environment Setup
|
||||
|
||||
@@ -96,10 +95,7 @@ exit 0
|
||||
|
||||
### Build Scripts
|
||||
|
||||
- **`build-electron.sh`**: Complete Electron build process
|
||||
- **`build-android.sh`**: Complete Android build process
|
||||
- **`build-electron-linux.sh`**: Linux Electron packaging (AppImage, .deb)
|
||||
- **`build-electron-mac.sh`**: macOS Electron packaging (standard, universal)
|
||||
|
||||
### Development Scripts
|
||||
|
||||
@@ -152,10 +148,8 @@ print_footer "Script Title"
|
||||
```
|
||||
|
||||
### Building Applications
|
||||
```bash
|
||||
# Build Electron
|
||||
./scripts/build-electron.sh
|
||||
|
||||
```bash
|
||||
# Build Android
|
||||
./scripts/build-android.sh
|
||||
|
||||
@@ -170,19 +164,21 @@ print_footer "Script Title"
|
||||
```
|
||||
|
||||
### Development Workflows
|
||||
|
||||
```bash
|
||||
# Start Electron development
|
||||
./scripts/electron-dev.sh
|
||||
# Start development
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Environment Variable Features
|
||||
|
||||
### Automatic Setup
|
||||
|
||||
All scripts automatically configure the correct environment variables for their build type:
|
||||
|
||||
```bash
|
||||
# Electron builds automatically get:
|
||||
export VITE_PLATFORM=electron
|
||||
# Capacitor builds automatically get:
|
||||
export VITE_PLATFORM=capacitor
|
||||
export VITE_PWA_ENABLED=false
|
||||
export VITE_DISABLE_PWA=true
|
||||
export DEBUG_MIGRATIONS=0
|
||||
@@ -214,7 +210,6 @@ validate_env_vars "VITE_API_URL" "VITE_DEBUG" || exit 1
|
||||
View current environment variables with the `--env` flag:
|
||||
|
||||
```bash
|
||||
./scripts/build-electron.sh --env
|
||||
./scripts/test-env.sh --env
|
||||
```
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/bin/bash
|
||||
# build-electron-linux.sh
|
||||
# Author: Matthew Raymer
|
||||
# Description: Electron Linux build script for TimeSafari application
|
||||
# This script builds Electron packages for Linux with support for different formats.
|
||||
#
|
||||
# Usage: ./scripts/build-electron-linux.sh [deb|prod]
|
||||
# - No argument: Builds AppImage
|
||||
# - deb: Builds .deb package
|
||||
# - prod: Builds production AppImage
|
||||
#
|
||||
# Exit Codes:
|
||||
# 1 - Build failed
|
||||
# 2 - Invalid argument
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Source common utilities
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args "$@"
|
||||
|
||||
# Parse build type argument
|
||||
BUILD_TYPE=${1:-"appimage"}
|
||||
PRODUCTION=false
|
||||
|
||||
case $BUILD_TYPE in
|
||||
"deb")
|
||||
BUILD_TARGET="deb"
|
||||
log_info "Building .deb package"
|
||||
;;
|
||||
"prod")
|
||||
BUILD_TARGET="AppImage"
|
||||
PRODUCTION=true
|
||||
log_info "Building production AppImage"
|
||||
;;
|
||||
"appimage"|"")
|
||||
BUILD_TARGET="AppImage"
|
||||
log_info "Building AppImage"
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid build type: $BUILD_TYPE"
|
||||
log_error "Usage: $0 [deb|prod]"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
# Print build header
|
||||
print_header "TimeSafari Electron Linux Build"
|
||||
log_info "Starting Linux build process at $(date)"
|
||||
log_info "Build type: $BUILD_TYPE"
|
||||
log_info "Build target: $BUILD_TARGET"
|
||||
log_info "Production mode: $PRODUCTION"
|
||||
|
||||
# Setup environment for Electron build
|
||||
setup_build_env "electron" "$PRODUCTION"
|
||||
|
||||
# Setup application directories
|
||||
setup_app_directories
|
||||
|
||||
# Load environment from .env file if it exists
|
||||
load_env_file ".env"
|
||||
|
||||
# Step 1: Build Electron application
|
||||
if [ "$PRODUCTION" = true ]; then
|
||||
safe_execute "Building production Electron application" "npm run build:electron-prod" || exit 1
|
||||
else
|
||||
safe_execute "Building Electron application" "npm run build:electron" || exit 1
|
||||
fi
|
||||
|
||||
# Step 2: Build package
|
||||
safe_execute "Building Linux package" "npx electron-builder --linux $BUILD_TARGET" || exit 1
|
||||
|
||||
# Print build summary
|
||||
log_success "Linux build completed successfully!"
|
||||
log_info "Package type: $BUILD_TARGET"
|
||||
print_footer "Linux Build"
|
||||
|
||||
# Exit with success
|
||||
exit 0
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/bin/bash
|
||||
# build-electron-mac.sh
|
||||
# Author: Matthew Raymer
|
||||
# Description: Electron Mac build script for TimeSafari application
|
||||
# This script builds Electron packages for macOS with support for universal builds.
|
||||
#
|
||||
# Usage: ./scripts/build-electron-mac.sh [universal]
|
||||
# - No argument: Builds standard Mac package
|
||||
# - universal: Builds universal Mac package (Intel + Apple Silicon)
|
||||
#
|
||||
# Exit Codes:
|
||||
# 1 - Build failed
|
||||
# 2 - Invalid argument
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Source common utilities
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args "$@"
|
||||
|
||||
# Parse build type argument
|
||||
BUILD_TYPE=${1:-"standard"}
|
||||
UNIVERSAL=false
|
||||
|
||||
case $BUILD_TYPE in
|
||||
"universal")
|
||||
UNIVERSAL=true
|
||||
log_info "Building universal Mac package (Intel + Apple Silicon)"
|
||||
;;
|
||||
"standard"|"")
|
||||
log_info "Building standard Mac package"
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid build type: $BUILD_TYPE"
|
||||
log_error "Usage: $0 [universal]"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
# Print build header
|
||||
print_header "TimeSafari Electron Mac Build"
|
||||
log_info "Starting Mac build process at $(date)"
|
||||
log_info "Build type: $BUILD_TYPE"
|
||||
log_info "Universal build: $UNIVERSAL"
|
||||
|
||||
# Setup environment for Electron build (production mode for packaging)
|
||||
setup_build_env "electron" "true"
|
||||
|
||||
# Setup application directories
|
||||
setup_app_directories
|
||||
|
||||
# Load environment from .env file if it exists
|
||||
load_env_file ".env"
|
||||
|
||||
# Step 1: Build Electron application
|
||||
safe_execute "Building Electron application" "npm run build:electron-prod" || exit 1
|
||||
|
||||
# Step 2: Build package
|
||||
if [ "$UNIVERSAL" = true ]; then
|
||||
safe_execute "Building universal Mac package" "npx electron-builder --mac --universal" || exit 1
|
||||
else
|
||||
safe_execute "Building Mac package" "npx electron-builder --mac" || exit 1
|
||||
fi
|
||||
|
||||
# Print build summary
|
||||
log_success "Mac build completed successfully!"
|
||||
log_info "Package type: $([ "$UNIVERSAL" = true ] && echo "Universal" || echo "Standard")"
|
||||
print_footer "Mac Build"
|
||||
|
||||
# Exit with success
|
||||
exit 0
|
||||
@@ -1,148 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('Starting electron build process...');
|
||||
|
||||
// Define paths
|
||||
const electronDistPath = path.join(__dirname, '..', 'dist-electron');
|
||||
const wwwPath = path.join(electronDistPath, 'www');
|
||||
|
||||
// Create www directory if it doesn't exist
|
||||
if (!fs.existsSync(wwwPath)) {
|
||||
fs.mkdirSync(wwwPath, { recursive: true });
|
||||
}
|
||||
|
||||
// Copy the Vite-built index.html to www directory
|
||||
const viteIndexPath = path.join(electronDistPath, 'index.html');
|
||||
const wwwIndexPath = path.join(wwwPath, 'index.html');
|
||||
|
||||
if (fs.existsSync(viteIndexPath)) {
|
||||
console.log('Copying Vite-built index.html to www directory...');
|
||||
fs.copyFileSync(viteIndexPath, wwwIndexPath);
|
||||
|
||||
// Remove the original index.html from dist-electron root
|
||||
fs.unlinkSync(viteIndexPath);
|
||||
console.log('Moved index.html to www directory');
|
||||
} else {
|
||||
console.error('Vite-built index.html not found at:', viteIndexPath);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Copy assets directory if it exists in dist-electron
|
||||
const assetsSrc = path.join(electronDistPath, 'assets');
|
||||
const assetsDest = path.join(wwwPath, 'assets');
|
||||
|
||||
if (fs.existsSync(assetsSrc)) {
|
||||
console.log('Moving assets directory to www...');
|
||||
if (fs.existsSync(assetsDest)) {
|
||||
fs.rmSync(assetsDest, { recursive: true, force: true });
|
||||
}
|
||||
fs.renameSync(assetsSrc, assetsDest);
|
||||
console.log('Moved assets directory to www');
|
||||
}
|
||||
|
||||
// Copy favicon if it exists
|
||||
const faviconSrc = path.join(electronDistPath, 'favicon.ico');
|
||||
const faviconDest = path.join(wwwPath, 'favicon.ico');
|
||||
|
||||
if (fs.existsSync(faviconSrc)) {
|
||||
console.log('Moving favicon to www...');
|
||||
fs.renameSync(faviconSrc, faviconDest);
|
||||
console.log('Moved favicon to www');
|
||||
}
|
||||
|
||||
// Remove service worker files from www directory
|
||||
const swFilesToRemove = [
|
||||
'sw.js',
|
||||
'sw.js.map',
|
||||
'workbox-*.js',
|
||||
'workbox-*.js.map',
|
||||
'registerSW.js',
|
||||
'manifest.webmanifest'
|
||||
];
|
||||
|
||||
console.log('Removing service worker files...');
|
||||
swFilesToRemove.forEach(pattern => {
|
||||
const files = fs.readdirSync(wwwPath).filter(file =>
|
||||
file.match(new RegExp(pattern.replace(/\*/g, '.*')))
|
||||
);
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(wwwPath, file);
|
||||
console.log(`Removing ${filePath}`);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.warn(`Could not remove ${filePath}:`, err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Also check and remove from assets directory
|
||||
if (fs.existsSync(assetsDest)) {
|
||||
swFilesToRemove.forEach(pattern => {
|
||||
const files = fs.readdirSync(assetsDest).filter(file =>
|
||||
file.match(new RegExp(pattern.replace(/\*/g, '.*')))
|
||||
);
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(assetsDest, file);
|
||||
console.log(`Removing ${filePath}`);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.warn(`Could not remove ${filePath}:`, err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Verify the final index.html structure
|
||||
const finalIndexContent = fs.readFileSync(wwwIndexPath, 'utf8');
|
||||
console.log('Final index.html structure:');
|
||||
console.log('- Has CSS link:', finalIndexContent.includes('<link rel="stylesheet"'));
|
||||
console.log('- Has main script:', finalIndexContent.includes('main.electron.js'));
|
||||
console.log('- No service worker references:', !finalIndexContent.includes('serviceWorker'));
|
||||
|
||||
// Copy main process files to the correct location
|
||||
console.log('Setting up main process files...');
|
||||
|
||||
// The main process files are already in the correct location
|
||||
// Just verify they exist and are ready
|
||||
const mainPath = path.join(electronDistPath, 'main.js');
|
||||
const preloadPath = path.join(electronDistPath, 'preload.js');
|
||||
|
||||
if (fs.existsSync(mainPath)) {
|
||||
console.log('Main process file ready at:', mainPath);
|
||||
} else {
|
||||
console.error('Main process file not found at:', mainPath);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(preloadPath)) {
|
||||
console.log('Preload script ready at:', preloadPath);
|
||||
} else {
|
||||
console.warn('Preload script not found at:', preloadPath);
|
||||
}
|
||||
|
||||
// Clean up any remaining files in dist-electron root (except main.js, preload.js, and www directory)
|
||||
const remainingFiles = fs.readdirSync(electronDistPath);
|
||||
remainingFiles.forEach(file => {
|
||||
if (file !== 'main.js' && file !== 'preload.js' && file !== 'www') {
|
||||
const filePath = path.join(electronDistPath, file);
|
||||
console.log(`Removing remaining file: ${file}`);
|
||||
try {
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
fs.rmSync(filePath, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Could not remove ${filePath}:`, err.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Electron build process completed successfully');
|
||||
console.log('Final structure:');
|
||||
console.log('- Main process:', path.join(electronDistPath, 'main.js'));
|
||||
console.log('- Preload script:', path.join(electronDistPath, 'preload.js'));
|
||||
console.log('- Web assets:', path.join(electronDistPath, 'www'));
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/bash
|
||||
# build-electron.sh
|
||||
# Author: Matthew Raymer
|
||||
# Description: Electron build script for TimeSafari application
|
||||
# This script handles the complete Electron build process including cleanup,
|
||||
# TypeScript compilation, Vite build, and Electron-specific setup.
|
||||
#
|
||||
# Exit Codes:
|
||||
# 1 - Cleanup failed
|
||||
# 2 - TypeScript compilation failed
|
||||
# 3 - Vite build failed
|
||||
# 4 - Electron build script failed
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Source common utilities
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args "$@"
|
||||
|
||||
# Print build header
|
||||
print_header "TimeSafari Electron Build Process"
|
||||
log_info "Starting Electron build process at $(date)"
|
||||
|
||||
# Setup environment for Electron build
|
||||
setup_build_env "electron"
|
||||
|
||||
# Setup application directories
|
||||
setup_app_directories
|
||||
|
||||
# Load environment from .env file if it exists
|
||||
load_env_file ".env"
|
||||
|
||||
# Step 1: Clean previous builds
|
||||
safe_execute "Cleaning previous builds" "npm run clean:electron" || exit 1
|
||||
|
||||
# Step 2: Compile TypeScript for Electron
|
||||
safe_execute "Compiling TypeScript for Electron" "npx tsc -p tsconfig.electron.json" || exit 2
|
||||
|
||||
# Step 3: Build with Vite
|
||||
safe_execute "Building with Vite" "npx vite build --config vite.config.electron.mts" || exit 3
|
||||
|
||||
# Step 4: Run Electron build script
|
||||
safe_execute "Running Electron build script" "node scripts/build-electron.js" || exit 4
|
||||
|
||||
# Print build summary
|
||||
log_success "Electron build completed successfully!"
|
||||
print_footer "Electron Build"
|
||||
|
||||
# Exit with success
|
||||
exit 0
|
||||
@@ -176,16 +176,6 @@ setup_build_env() {
|
||||
log_debug "Set VITE_GIT_HASH=$git_hash"
|
||||
|
||||
case $build_type in
|
||||
"electron")
|
||||
export VITE_PLATFORM=electron
|
||||
export VITE_PWA_ENABLED=false
|
||||
export VITE_DISABLE_PWA=true
|
||||
export DEBUG_MIGRATIONS=0
|
||||
if [ "$production" = true ]; then
|
||||
export NODE_ENV=production
|
||||
log_debug "Set production mode for Electron"
|
||||
fi
|
||||
;;
|
||||
"capacitor")
|
||||
export VITE_PLATFORM=capacitor
|
||||
export VITE_PWA_ENABLED=false
|
||||
@@ -227,7 +217,6 @@ setup_app_directories() {
|
||||
|
||||
# Create build directories if they don't exist
|
||||
mkdir -p dist
|
||||
mkdir -p dist-electron
|
||||
|
||||
log_debug "Application directories created"
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
# electron-dev.sh
|
||||
# Author: Matthew Raymer
|
||||
# Description: Electron development script for TimeSafari application
|
||||
# This script builds the application and starts Electron for development.
|
||||
#
|
||||
# Exit Codes:
|
||||
# 1 - Build failed
|
||||
# 2 - Electron start failed
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
# Source common utilities
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Parse command line arguments
|
||||
parse_args "$@"
|
||||
|
||||
# Print dev header
|
||||
print_header "TimeSafari Electron Development"
|
||||
log_info "Starting Electron development at $(date)"
|
||||
|
||||
# Setup environment for Electron development
|
||||
setup_build_env "electron"
|
||||
|
||||
# Setup application directories
|
||||
setup_app_directories
|
||||
|
||||
# Load environment from .env file if it exists
|
||||
load_env_file ".env"
|
||||
|
||||
# Step 1: Build the application
|
||||
safe_execute "Building application" "npm run build" || exit 1
|
||||
|
||||
# Step 2: Start Electron
|
||||
safe_execute "Starting Electron" "electron ." || exit 2
|
||||
|
||||
# Print dev summary
|
||||
log_success "Electron development session ended"
|
||||
print_footer "Electron Development"
|
||||
|
||||
# Exit with success
|
||||
exit 0
|
||||
@@ -17,37 +17,34 @@ parse_args "$@"
|
||||
print_header "Environment Variable Test"
|
||||
log_info "Testing environment variable handling at $(date)"
|
||||
|
||||
# Test 1: Electron environment
|
||||
log_info "Test 1: Setting up Electron environment..."
|
||||
setup_build_env "electron"
|
||||
print_env_vars "VITE_"
|
||||
|
||||
# Test 2: Capacitor environment
|
||||
log_info "Test 2: Setting up Capacitor environment..."
|
||||
# Test 1: Capacitor environment
|
||||
log_info "Test 1: Setting up Capacitor environment..."
|
||||
setup_build_env "capacitor"
|
||||
print_env_vars "VITE_"
|
||||
echo ""
|
||||
|
||||
# Test 3: Web environment
|
||||
log_info "Test 3: Setting up Web environment..."
|
||||
# Test 2: Web environment
|
||||
log_info "Test 2: Setting up Web environment..."
|
||||
setup_build_env "web"
|
||||
print_env_vars "VITE_"
|
||||
echo ""
|
||||
|
||||
# Test 4: Production Electron environment
|
||||
log_info "Test 4: Setting up Production Electron environment..."
|
||||
setup_build_env "electron" "true"
|
||||
# Test 3: Production Capacitor environment
|
||||
log_info "Test 3: Setting up Production Capacitor environment..."
|
||||
setup_build_env "capacitor" "true"
|
||||
print_env_vars "VITE_"
|
||||
print_env_vars "NODE_ENV"
|
||||
echo ""
|
||||
|
||||
# Test 5: Application directories
|
||||
log_info "Test 5: Setting up application directories..."
|
||||
# Test 4: Application directories
|
||||
log_info "Test 4: Setting up application directories..."
|
||||
setup_app_directories
|
||||
|
||||
# Test 6: Load .env file (if it exists)
|
||||
log_info "Test 6: Loading .env file..."
|
||||
# Test 5: Load .env file (if it exists)
|
||||
log_info "Test 5: Loading .env file..."
|
||||
load_env_file ".env"
|
||||
|
||||
# Test 7: Git hash
|
||||
log_info "Test 7: Getting git hash..."
|
||||
# Test 6: Git hash
|
||||
log_info "Test 6: Getting git hash..."
|
||||
GIT_HASH=$(get_git_hash)
|
||||
log_info "Git hash: $GIT_HASH"
|
||||
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
// Check if running in dev mode
|
||||
const isDev = process.argv.includes("--inspect");
|
||||
|
||||
function createWindow() {
|
||||
// Add before createWindow function
|
||||
const preloadPath = path.join(__dirname, "preload.js");
|
||||
logger.log("Checking preload path:", preloadPath);
|
||||
logger.log("Preload exists:", fs.existsSync(preloadPath));
|
||||
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: true,
|
||||
allowRunningInsecureContent: false,
|
||||
preload: path.join(__dirname, "preload.js"),
|
||||
},
|
||||
});
|
||||
|
||||
// Always open DevTools for now
|
||||
mainWindow.webContents.openDevTools();
|
||||
|
||||
// Intercept requests to fix asset paths
|
||||
mainWindow.webContents.session.webRequest.onBeforeRequest(
|
||||
{
|
||||
urls: [
|
||||
"file://*/*/assets/*",
|
||||
"file://*/assets/*",
|
||||
"file:///assets/*", // Catch absolute paths
|
||||
"<all_urls>", // Catch all URLs as a fallback
|
||||
],
|
||||
},
|
||||
(details, callback) => {
|
||||
let url = details.url;
|
||||
|
||||
// Handle paths that don't start with file://
|
||||
if (!url.startsWith("file://") && url.includes("/assets/")) {
|
||||
url = `file://${path.join(__dirname, "www", url)}`;
|
||||
}
|
||||
|
||||
// Handle absolute paths starting with /assets/
|
||||
if (url.includes("/assets/") && !url.includes("/www/assets/")) {
|
||||
const baseDir = url.includes("dist-electron")
|
||||
? url.substring(
|
||||
0,
|
||||
url.indexOf("/dist-electron") + "/dist-electron".length,
|
||||
)
|
||||
: `file://${__dirname}`;
|
||||
const assetPath = url.split("/assets/")[1];
|
||||
const newUrl = `${baseDir}/www/assets/${assetPath}`;
|
||||
callback({ redirectURL: newUrl });
|
||||
return;
|
||||
}
|
||||
|
||||
callback({}); // No redirect for other URLs
|
||||
},
|
||||
);
|
||||
|
||||
if (isDev) {
|
||||
// Debug info
|
||||
logger.log("Debug Info:");
|
||||
logger.log("Running in dev mode:", isDev);
|
||||
logger.log("App is packaged:", app.isPackaged);
|
||||
logger.log("Process resource path:", process.resourcesPath);
|
||||
logger.log("App path:", app.getAppPath());
|
||||
logger.log("__dirname:", __dirname);
|
||||
logger.log("process.cwd():", process.cwd());
|
||||
}
|
||||
|
||||
const indexPath = path.join(__dirname, "www", "index.html");
|
||||
|
||||
if (isDev) {
|
||||
logger.log("Loading index from:", indexPath);
|
||||
logger.log("www path:", path.join(__dirname, "www"));
|
||||
logger.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||
}
|
||||
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
logger.error(`Index file not found at: ${indexPath}`);
|
||||
throw new Error("Index file not found");
|
||||
}
|
||||
|
||||
// Add CSP headers to allow API connections
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived(
|
||||
(details, callback) => {
|
||||
callback({
|
||||
responseHeaders: {
|
||||
...details.responseHeaders,
|
||||
"Content-Security-Policy": [
|
||||
"default-src 'self';" +
|
||||
"connect-src 'self' https://api.endorser.ch https://*.timesafari.app;" +
|
||||
"img-src 'self' data: https: blob:;" +
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval';" +
|
||||
"style-src 'self' 'unsafe-inline';" +
|
||||
"font-src 'self' data:;",
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Load the index.html
|
||||
mainWindow
|
||||
.loadFile(indexPath)
|
||||
.then(() => {
|
||||
logger.log("Successfully loaded index.html");
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
logger.log("DevTools opened - running in dev mode");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("Failed to load index.html:", err);
|
||||
logger.error("Attempted path:", indexPath);
|
||||
});
|
||||
|
||||
// Listen for console messages from the renderer
|
||||
mainWindow.webContents.on("console-message", (_event, level, message) => {
|
||||
logger.log("Renderer Console:", message);
|
||||
});
|
||||
|
||||
// Add right after creating the BrowserWindow
|
||||
mainWindow.webContents.on(
|
||||
"did-fail-load",
|
||||
(event, errorCode, errorDescription) => {
|
||||
logger.error("Page failed to load:", errorCode, errorDescription);
|
||||
},
|
||||
);
|
||||
|
||||
mainWindow.webContents.on("preload-error", (event, preloadPath, error) => {
|
||||
logger.error("Preload script error:", preloadPath, error);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on(
|
||||
"console-message",
|
||||
(event, level, message, line, sourceId) => {
|
||||
logger.log("Renderer Console:", line, sourceId, message);
|
||||
},
|
||||
);
|
||||
|
||||
// Enable remote debugging when in dev mode
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle app ready
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
// Handle all windows closed
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle any errors
|
||||
process.on("uncaughtException", (error) => {
|
||||
logger.error("Uncaught Exception:", error);
|
||||
});
|
||||
@@ -1,221 +0,0 @@
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
// Simple logger implementation
|
||||
const logger = {
|
||||
// eslint-disable-next-line no-console
|
||||
log: (...args: unknown[]) => console.log(...args),
|
||||
// eslint-disable-next-line no-console
|
||||
error: (...args: unknown[]) => console.error(...args),
|
||||
// eslint-disable-next-line no-console
|
||||
info: (...args: unknown[]) => console.info(...args),
|
||||
// eslint-disable-next-line no-console
|
||||
warn: (...args: unknown[]) => console.warn(...args),
|
||||
// eslint-disable-next-line no-console
|
||||
debug: (...args: unknown[]) => console.debug(...args),
|
||||
};
|
||||
|
||||
// Check if running in dev mode
|
||||
const isDev = process.argv.includes("--inspect");
|
||||
|
||||
async function createWindow(): Promise<void> {
|
||||
// Add before createWindow function
|
||||
const preloadPath = app.isPackaged
|
||||
? path.join(app.getAppPath(), "dist-electron", "preload.js")
|
||||
: path.join(__dirname, "preload.js");
|
||||
logger.log("Checking preload path:", preloadPath);
|
||||
logger.log("Preload exists:", fs.existsSync(preloadPath));
|
||||
|
||||
// Log environment and paths
|
||||
logger.log("process.cwd():", process.cwd());
|
||||
logger.log("__dirname:", __dirname);
|
||||
logger.log("app.getAppPath():", app.getAppPath());
|
||||
logger.log("app.isPackaged:", app.isPackaged);
|
||||
|
||||
// List files in __dirname and __dirname/www
|
||||
try {
|
||||
logger.log("Files in __dirname:", fs.readdirSync(__dirname));
|
||||
const wwwDir = path.join(__dirname, "www");
|
||||
if (fs.existsSync(wwwDir)) {
|
||||
logger.log("Files in www:", fs.readdirSync(wwwDir));
|
||||
} else {
|
||||
logger.log("www directory does not exist in __dirname");
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Error reading directories:", e);
|
||||
}
|
||||
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: true,
|
||||
allowRunningInsecureContent: false,
|
||||
preload: preloadPath,
|
||||
},
|
||||
});
|
||||
|
||||
// Always open DevTools for now
|
||||
mainWindow.webContents.openDevTools();
|
||||
|
||||
// Intercept requests to robustly fix asset paths for Electron
|
||||
mainWindow.webContents.session.webRequest.onBeforeRequest(
|
||||
{ urls: ["*://*/*"] },
|
||||
(details, callback) => {
|
||||
const url = details.url;
|
||||
logger.log('[main.ts] Intercepted request:', url);
|
||||
|
||||
// Match both file:///assets/... and /assets/...
|
||||
const assetMatch = url.match(/(?:file:\/\/\/|file:\/\/|https?:\/\/[^\/]+)?\/assets\/(.+)/);
|
||||
if (assetMatch) {
|
||||
const assetRelPath = assetMatch[1];
|
||||
const assetAbsPath = path.join(app.getAppPath(), "dist-electron", "www", "assets", assetRelPath);
|
||||
logger.log('[main.ts] Asset request detected:', {
|
||||
originalUrl: url,
|
||||
assetRelPath,
|
||||
assetAbsPath,
|
||||
exists: fs.existsSync(assetAbsPath)
|
||||
});
|
||||
|
||||
if (fs.existsSync(assetAbsPath)) {
|
||||
const newUrl = `file://${assetAbsPath}`;
|
||||
logger.log('[main.ts] Redirecting to:', newUrl);
|
||||
callback({ redirectURL: newUrl });
|
||||
return;
|
||||
} else {
|
||||
logger.error('[main.ts] Asset file not found:', assetAbsPath);
|
||||
}
|
||||
}
|
||||
callback({});
|
||||
}
|
||||
);
|
||||
|
||||
if (isDev) {
|
||||
// Debug info
|
||||
logger.log("Debug Info:");
|
||||
logger.log("Running in dev mode:", isDev);
|
||||
logger.log("App is packaged:", app.isPackaged);
|
||||
logger.log("Process resource path:", process.resourcesPath);
|
||||
logger.log("App path:", app.getAppPath());
|
||||
logger.log("__dirname:", __dirname);
|
||||
logger.log("process.cwd():", process.cwd());
|
||||
}
|
||||
|
||||
let indexPath: string;
|
||||
if (app.isPackaged) {
|
||||
indexPath = path.join(
|
||||
app.getAppPath(),
|
||||
"dist-electron",
|
||||
"www",
|
||||
"index.html",
|
||||
);
|
||||
logger.log("[main.ts] Using packaged indexPath:", indexPath);
|
||||
} else {
|
||||
indexPath = path.resolve(
|
||||
process.cwd(),
|
||||
"dist-electron",
|
||||
"www",
|
||||
"index.html",
|
||||
);
|
||||
logger.log("[main.ts] Using dev indexPath:", indexPath);
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
logger.error("[main.ts] Dev index.html not found:", indexPath);
|
||||
throw new Error("Index file not found");
|
||||
}
|
||||
}
|
||||
|
||||
if (isDev) {
|
||||
logger.log("Loading index from:", indexPath);
|
||||
logger.log("www path:", path.join(__dirname, "www"));
|
||||
logger.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||
}
|
||||
|
||||
// Add CSP headers to allow API connections
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived(
|
||||
(details, callback) => {
|
||||
callback({
|
||||
responseHeaders: {
|
||||
...details.responseHeaders,
|
||||
"Content-Security-Policy": [
|
||||
"default-src 'self';" +
|
||||
"connect-src 'self' https://api.endorser.ch https://*.timesafari.app;" +
|
||||
"img-src 'self' data: https: blob:;" +
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval';" +
|
||||
"style-src 'self' 'unsafe-inline';" +
|
||||
"font-src 'self' data:;",
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Load the index.html with the correct base URL for assets
|
||||
const baseURL = `file://${path.dirname(indexPath)}/`;
|
||||
logger.log('[main.ts] Loading with base URL:', baseURL);
|
||||
|
||||
try {
|
||||
await mainWindow.loadURL(`file://${indexPath}`);
|
||||
logger.log("Successfully loaded index.html");
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
logger.log("DevTools opened - running in dev mode");
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Failed to load index.html:", err);
|
||||
logger.error("Attempted path:", indexPath);
|
||||
}
|
||||
|
||||
// Listen for console messages from the renderer
|
||||
mainWindow.webContents.on("console-message", (_event, _level, message) => {
|
||||
logger.log("Renderer Console:", message);
|
||||
});
|
||||
|
||||
// Add right after creating the BrowserWindow
|
||||
mainWindow.webContents.on(
|
||||
"did-fail-load",
|
||||
(_event, errorCode, errorDescription) => {
|
||||
logger.error("Page failed to load:", errorCode, errorDescription);
|
||||
},
|
||||
);
|
||||
|
||||
mainWindow.webContents.on("preload-error", (_event, preloadPath, error) => {
|
||||
logger.error("Preload script error:", preloadPath, error);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on(
|
||||
"console-message",
|
||||
(_event, _level, message, line, sourceId) => {
|
||||
logger.log("Renderer Console:", line, sourceId, message);
|
||||
},
|
||||
);
|
||||
|
||||
// Enable remote debugging when in dev mode
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle app ready
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
// Handle all windows closed
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle any errors
|
||||
process.on("uncaughtException", (error) => {
|
||||
logger.error("Uncaught Exception:", error);
|
||||
});
|
||||
@@ -1,91 +0,0 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
const logger = {
|
||||
log: (message, ...args) => {
|
||||
// Always log in development, log with context in production
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
/* eslint-disable no-console */
|
||||
console.log(`[Preload] ${message}`, ...args);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
},
|
||||
warn: (message, ...args) => {
|
||||
// Always log warnings
|
||||
/* eslint-disable no-console */
|
||||
console.warn(`[Preload] ${message}`, ...args);
|
||||
/* eslint-enable no-console */
|
||||
},
|
||||
error: (message, ...args) => {
|
||||
// Always log errors
|
||||
/* eslint-disable no-console */
|
||||
console.error(`[Preload] ${message}`, ...args);
|
||||
/* eslint-enable no-console */
|
||||
},
|
||||
info: (message, ...args) => {
|
||||
// Always log info in development, log with context in production
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
/* eslint-disable no-console */
|
||||
console.info(`[Preload] ${message}`, ...args);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Use a more direct path resolution approach
|
||||
const getPath = (pathType) => {
|
||||
switch (pathType) {
|
||||
case "userData":
|
||||
return (
|
||||
process.env.APPDATA ||
|
||||
(process.platform === "darwin"
|
||||
? `${process.env.HOME}/Library/Application Support`
|
||||
: `${process.env.HOME}/.local/share`)
|
||||
);
|
||||
case "home":
|
||||
return process.env.HOME;
|
||||
case "appPath":
|
||||
return process.resourcesPath;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
logger.info("Preload script starting...");
|
||||
|
||||
// Force electron platform in the renderer process
|
||||
window.process = { env: { VITE_PLATFORM: "electron" } };
|
||||
|
||||
try {
|
||||
contextBridge.exposeInMainWorld("electronAPI", {
|
||||
// Path utilities
|
||||
getPath,
|
||||
|
||||
// IPC functions
|
||||
send: (channel, data) => {
|
||||
const validChannels = ["toMain"];
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.send(channel, data);
|
||||
}
|
||||
},
|
||||
receive: (channel, func) => {
|
||||
const validChannels = ["fromMain"];
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.on(channel, (event, ...args) => func(...args));
|
||||
}
|
||||
},
|
||||
// Environment info
|
||||
env: {
|
||||
isElectron: true,
|
||||
isDev: process.env.NODE_ENV === "development",
|
||||
platform: "electron", // Explicitly set platform
|
||||
},
|
||||
// Path utilities
|
||||
getBasePath: () => {
|
||||
return process.env.NODE_ENV === "development" ? "/" : "./";
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Preload script completed successfully");
|
||||
} catch (error) {
|
||||
logger.error("Error in preload script:", error);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import './assets/styles/tailwind.css';
|
||||
import { initializeApp } from "./main.common";
|
||||
import { logger } from "./utils/logger";
|
||||
|
||||
const platform = process.env.VITE_PLATFORM;
|
||||
const pwa_enabled = process.env.VITE_PWA_ENABLED === "true";
|
||||
|
||||
logger.info("[Electron] Initializing app");
|
||||
logger.info("[Electron] Platform:", { platform });
|
||||
logger.info("[Electron] PWA enabled:", { pwa_enabled });
|
||||
|
||||
if (pwa_enabled) {
|
||||
logger.warn("[Electron] PWA is enabled, but not supported in electron");
|
||||
}
|
||||
|
||||
const app = initializeApp();
|
||||
app.mount("#app");
|
||||
@@ -6,7 +6,7 @@ const platform = process.env.VITE_PLATFORM;
|
||||
const pwa_enabled = process.env.VITE_PWA_ENABLED === "true";
|
||||
|
||||
// Only import service worker for web builds
|
||||
if (platform !== "electron" && pwa_enabled) {
|
||||
if (pwa_enabled) {
|
||||
import("./registerServiceWorker"); // Web PWA support
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,10 @@
|
||||
|
||||
import { register } from "register-service-worker";
|
||||
|
||||
// Check if we're in an Electron environment
|
||||
const isElectron =
|
||||
process.env.VITE_PLATFORM === "electron" ||
|
||||
process.env.VITE_DISABLE_PWA === "true" ||
|
||||
window.navigator.userAgent.toLowerCase().includes("electron");
|
||||
|
||||
// Only register service worker if:
|
||||
// 1. Not in Electron
|
||||
// 2. PWA is explicitly enabled
|
||||
// 3. In production mode
|
||||
// 1. PWA is explicitly enabled
|
||||
// 2. In production mode
|
||||
if (
|
||||
!isElectron &&
|
||||
process.env.VITE_PWA_ENABLED === "true" &&
|
||||
process.env.NODE_ENV === "production"
|
||||
) {
|
||||
@@ -45,11 +37,9 @@ if (
|
||||
} else {
|
||||
console.log(
|
||||
`Service worker registration skipped - ${
|
||||
isElectron
|
||||
? "running in Electron"
|
||||
: process.env.VITE_PWA_ENABLED !== "true"
|
||||
? "PWA not enabled"
|
||||
: "not in production mode"
|
||||
process.env.VITE_PWA_ENABLED !== "true"
|
||||
? "PWA not enabled"
|
||||
: "not in production mode"
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -277,14 +277,9 @@ const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
];
|
||||
|
||||
const isElectron = window.location.protocol === "file:";
|
||||
const initialPath = isElectron
|
||||
? window.location.pathname.split("/dist-electron/www/")[1] || "/"
|
||||
: window.location.pathname;
|
||||
const initialPath = window.location.pathname;
|
||||
|
||||
const history = isElectron
|
||||
? createMemoryHistory() // Memory history for Electron
|
||||
: createWebHistory("/"); // Add base path for web apps
|
||||
const history = createWebHistory("/"); // Add base path for web apps
|
||||
|
||||
/** @type {*} */
|
||||
const router = createRouter({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PlatformService } from "./PlatformService";
|
||||
import { WebPlatformService } from "./platforms/WebPlatformService";
|
||||
import { CapacitorPlatformService } from "./platforms/CapacitorPlatformService";
|
||||
import { ElectronPlatformService } from "./platforms/ElectronPlatformService";
|
||||
|
||||
/**
|
||||
* Factory class for creating platform-specific service implementations.
|
||||
@@ -10,7 +9,6 @@ import { ElectronPlatformService } from "./platforms/ElectronPlatformService";
|
||||
* The factory determines which platform implementation to use based on the VITE_PLATFORM
|
||||
* environment variable. Supported platforms are:
|
||||
* - capacitor: Mobile platform using Capacitor
|
||||
* - electron: Desktop platform using Electron
|
||||
* - web: Default web platform (fallback)
|
||||
*
|
||||
* @example
|
||||
@@ -39,9 +37,6 @@ export class PlatformServiceFactory {
|
||||
case "capacitor":
|
||||
PlatformServiceFactory.instance = new CapacitorPlatformService();
|
||||
break;
|
||||
case "electron":
|
||||
PlatformServiceFactory.instance = new ElectronPlatformService();
|
||||
break;
|
||||
case "web":
|
||||
default:
|
||||
PlatformServiceFactory.instance = new WebPlatformService();
|
||||
|
||||
@@ -244,13 +244,15 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
* @returns Platform capabilities object
|
||||
*/
|
||||
getCapabilities(): PlatformCapabilities {
|
||||
const platform = Capacitor.getPlatform();
|
||||
|
||||
return {
|
||||
hasFileSystem: true,
|
||||
hasCamera: true,
|
||||
isMobile: true,
|
||||
isIOS: Capacitor.getPlatform() === "ios",
|
||||
hasFileDownload: false,
|
||||
needsFileHandlingInstructions: true,
|
||||
isMobile: true, // Capacitor is always mobile
|
||||
isIOS: platform === "ios",
|
||||
hasFileDownload: false, // Mobile platforms need sharing
|
||||
needsFileHandlingInstructions: true, // Mobile needs instructions
|
||||
isNativeApp: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,358 +0,0 @@
|
||||
import {
|
||||
ImageResult,
|
||||
PlatformService,
|
||||
PlatformCapabilities,
|
||||
} from "../PlatformService";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { QueryExecResult, SqlValue } from "@/interfaces/database";
|
||||
import {
|
||||
SQLiteConnection,
|
||||
SQLiteDBConnection,
|
||||
CapacitorSQLite,
|
||||
Changes,
|
||||
} from "@capacitor-community/sqlite";
|
||||
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
||||
|
||||
interface Migration {
|
||||
name: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform service implementation for Electron (desktop) platform.
|
||||
* Provides native desktop functionality through Electron and Capacitor plugins for:
|
||||
* - File system operations (TODO)
|
||||
* - Camera integration (TODO)
|
||||
* - SQLite database operations
|
||||
* - System-level features (TODO)
|
||||
*/
|
||||
export class ElectronPlatformService implements PlatformService {
|
||||
private sqlite: SQLiteConnection;
|
||||
private db: SQLiteDBConnection | null = null;
|
||||
private dbName = "timesafari.db";
|
||||
private initialized = false;
|
||||
|
||||
constructor() {
|
||||
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
||||
}
|
||||
|
||||
private async initializeDatabase(): Promise<void> {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create/Open database
|
||||
this.db = await this.sqlite.createConnection(
|
||||
this.dbName,
|
||||
false,
|
||||
"no-encryption",
|
||||
1,
|
||||
false,
|
||||
);
|
||||
|
||||
await this.db.open();
|
||||
|
||||
// Set journal mode to WAL for better performance
|
||||
await this.db.execute("PRAGMA journal_mode=WAL;");
|
||||
|
||||
// Run migrations
|
||||
await this.runMigrations();
|
||||
|
||||
this.initialized = true;
|
||||
logger.log(
|
||||
"[ElectronPlatformService] SQLite database initialized successfully",
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"[ElectronPlatformService] Error initializing SQLite database:",
|
||||
error,
|
||||
);
|
||||
throw new Error(
|
||||
"[ElectronPlatformService] Failed to initialize database",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async runMigrations(): Promise<void> {
|
||||
if (!this.db) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
// Create migrations table if it doesn't exist
|
||||
await this.db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
|
||||
// Get list of executed migrations
|
||||
const result = await this.db.query("SELECT name FROM migrations;");
|
||||
const executedMigrations = new Set(
|
||||
result.values?.map((row) => row[0]) || [],
|
||||
);
|
||||
|
||||
// Run pending migrations in order
|
||||
const migrations: Migration[] = [
|
||||
{
|
||||
name: "001_initial",
|
||||
sql: `
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dateCreated TEXT NOT NULL,
|
||||
derivationPath TEXT,
|
||||
did TEXT NOT NULL,
|
||||
identityEncrBase64 TEXT,
|
||||
mnemonicEncrBase64 TEXT,
|
||||
passkeyCredIdHex TEXT,
|
||||
publicKeyHex TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_did ON accounts(did);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS secret (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
secretBase64 TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
accountDid TEXT,
|
||||
activeDid TEXT,
|
||||
apiServer TEXT,
|
||||
filterFeedByNearby BOOLEAN,
|
||||
filterFeedByVisible BOOLEAN,
|
||||
finishedOnboarding BOOLEAN,
|
||||
firstName TEXT,
|
||||
hideRegisterPromptOnNewContact BOOLEAN,
|
||||
isRegistered BOOLEAN,
|
||||
lastName TEXT,
|
||||
lastAckedOfferToUserJwtId TEXT,
|
||||
lastAckedOfferToUserProjectsJwtId TEXT,
|
||||
lastNotifiedClaimId TEXT,
|
||||
lastViewedClaimId TEXT,
|
||||
notifyingNewActivityTime TEXT,
|
||||
notifyingReminderMessage TEXT,
|
||||
notifyingReminderTime TEXT,
|
||||
partnerApiServer TEXT,
|
||||
passkeyExpirationMinutes INTEGER,
|
||||
profileImageUrl TEXT,
|
||||
searchBoxes TEXT,
|
||||
showContactGivesInline BOOLEAN,
|
||||
showGeneralAdvanced BOOLEAN,
|
||||
showShortcutBvc BOOLEAN,
|
||||
vapid TEXT,
|
||||
warnIfProdServer BOOLEAN,
|
||||
warnIfTestServer BOOLEAN,
|
||||
webPushServer TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_settings_accountDid ON settings(accountDid);
|
||||
|
||||
INSERT INTO settings (id, apiServer) VALUES (1, '${DEFAULT_ENDORSER_API_SERVER}');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contacts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
did TEXT NOT NULL,
|
||||
name TEXT,
|
||||
contactMethods TEXT,
|
||||
nextPubKeyHashB64 TEXT,
|
||||
notes TEXT,
|
||||
profileImageUrl TEXT,
|
||||
publicKeyBase64 TEXT,
|
||||
seesMe BOOLEAN,
|
||||
registered BOOLEAN
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_did ON contacts(did);
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS logs (
|
||||
date TEXT PRIMARY KEY,
|
||||
message TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS temp (
|
||||
id TEXT PRIMARY KEY,
|
||||
blobB64 TEXT
|
||||
);
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
for (const migration of migrations) {
|
||||
if (!executedMigrations.has(migration.name)) {
|
||||
await this.db.execute(migration.sql);
|
||||
await this.db.run("INSERT INTO migrations (name) VALUES (?)", [
|
||||
migration.name,
|
||||
]);
|
||||
logger.log(`Migration ${migration.name} executed successfully`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the capabilities of the Electron platform
|
||||
* @returns Platform capabilities object
|
||||
*/
|
||||
getCapabilities(): PlatformCapabilities {
|
||||
return {
|
||||
hasFileSystem: false, // Not implemented yet
|
||||
hasCamera: false, // Not implemented yet
|
||||
isMobile: false,
|
||||
isIOS: false,
|
||||
hasFileDownload: false, // Not implemented yet
|
||||
needsFileHandlingInstructions: false,
|
||||
isNativeApp: true, // Electron is a native app
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file from the filesystem.
|
||||
* @param _path - Path to the file to read
|
||||
* @returns Promise that should resolve to file contents
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement file reading using Electron's file system API
|
||||
*/
|
||||
async readFile(_path: string): Promise<string> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes content to a file.
|
||||
* @param _path - Path where to write the file
|
||||
* @param _content - Content to write to the file
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement file writing using Electron's file system API
|
||||
*/
|
||||
async writeFile(_path: string, _content: string): Promise<void> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes content to a file and opens the system share dialog.
|
||||
* @param _fileName - Name of the file to create
|
||||
* @param _content - Content to write to the file
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement using Electron's dialog and file system APIs
|
||||
*/
|
||||
async writeAndShareFile(_fileName: string, _content: string): Promise<void> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file from the filesystem.
|
||||
* @param _path - Path to the file to delete
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement file deletion using Electron's file system API
|
||||
*/
|
||||
async deleteFile(_path: string): Promise<void> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists files in the specified directory.
|
||||
* @param _directory - Path to the directory to list
|
||||
* @returns Promise that should resolve to array of filenames
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement directory listing using Electron's file system API
|
||||
*/
|
||||
async listFiles(_directory: string): Promise<string[]> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Should open system camera to take a picture.
|
||||
* @returns Promise that should resolve to captured image data
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement camera access using Electron's media APIs
|
||||
*/
|
||||
async takePicture(): Promise<ImageResult> {
|
||||
logger.error("takePicture not implemented in Electron platform");
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Should open system file picker for selecting an image.
|
||||
* @returns Promise that should resolve to selected image data
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement file picker using Electron's dialog API
|
||||
*/
|
||||
async pickImage(): Promise<ImageResult> {
|
||||
logger.error("pickImage not implemented in Electron platform");
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Should handle deep link URLs for the desktop application.
|
||||
* @param _url - The deep link URL to handle
|
||||
* @throws Error with "Not implemented" message
|
||||
* @todo Implement deep link handling using Electron's protocol handler
|
||||
*/
|
||||
async handleDeepLink(_url: string): Promise<void> {
|
||||
logger.error("handleDeepLink not implemented in Electron platform");
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PlatformService.dbQuery
|
||||
*/
|
||||
async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
||||
await this.initializeDatabase();
|
||||
if (!this.db) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.db.query(sql, params || []);
|
||||
const values = result.values || [];
|
||||
return {
|
||||
columns: [], // SQLite plugin doesn't provide column names in query result
|
||||
values: values as SqlValue[][],
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error("Error executing query:", error);
|
||||
throw new Error(
|
||||
`Database query failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PlatformService.dbExec
|
||||
*/
|
||||
async dbExec(
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
): Promise<{ changes: number; lastId?: number }> {
|
||||
await this.initializeDatabase();
|
||||
if (!this.db) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.db.run(sql, params || []);
|
||||
const changes = result.changes as Changes;
|
||||
return {
|
||||
changes: changes?.changes || 0,
|
||||
lastId: changes?.lastId,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error("Error executing statement:", error);
|
||||
throw new Error(
|
||||
`Database execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the camera between front and back cameras.
|
||||
* @returns Promise that resolves when the camera is rotated
|
||||
* @throws Error indicating camera rotation is not implemented in Electron
|
||||
*/
|
||||
async rotateCamera(): Promise<void> {
|
||||
throw new Error("Camera rotation not implemented in Electron platform");
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "ES2020",
|
||||
"outDir": "dist-electron",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"types": ["vite/client"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"typeRoots": ["./node_modules/@types", "./src/types"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue"
|
||||
]
|
||||
}
|
||||
@@ -13,28 +13,27 @@ const __dirname = path.dirname(__filename);
|
||||
|
||||
export async function createBuildConfig(mode: string): Promise<UserConfig> {
|
||||
const appConfig = await loadAppConfig();
|
||||
const isElectron = mode === "electron";
|
||||
const isCapacitor = mode === "capacitor";
|
||||
const isNative = isElectron || isCapacitor;
|
||||
const isNative = isCapacitor;
|
||||
|
||||
// Explicitly set platform and disable PWA for Electron
|
||||
// Set platform and disable PWA for native platforms
|
||||
process.env.VITE_PLATFORM = mode;
|
||||
process.env.VITE_PWA_ENABLED = isElectron ? 'false' : 'true';
|
||||
process.env.VITE_DISABLE_PWA = isElectron ? 'true' : 'false';
|
||||
process.env.VITE_PWA_ENABLED = isCapacitor ? 'false' : 'true';
|
||||
process.env.VITE_DISABLE_PWA = isCapacitor ? 'true' : 'false';
|
||||
|
||||
if (isElectron || isCapacitor) {
|
||||
if (isCapacitor) {
|
||||
process.env.VITE_PWA_ENABLED = 'false';
|
||||
}
|
||||
|
||||
return {
|
||||
base: isElectron ? "./" : "/",
|
||||
base: "/",
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
port: parseInt(process.env.VITE_PORT || "8080"),
|
||||
fs: { strict: false },
|
||||
},
|
||||
build: {
|
||||
outDir: isElectron ? "dist-electron" : "dist",
|
||||
outDir: "dist",
|
||||
assetsDir: 'assets',
|
||||
chunkSizeWarningLimit: 1000,
|
||||
rollupOptions: {
|
||||
@@ -57,9 +56,9 @@ export async function createBuildConfig(mode: string): Promise<UserConfig> {
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.VITE_PLATFORM': JSON.stringify(mode),
|
||||
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isElectron),
|
||||
'process.env.VITE_DISABLE_PWA': JSON.stringify(isElectron),
|
||||
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
|
||||
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isCapacitor),
|
||||
'process.env.VITE_DISABLE_PWA': JSON.stringify(isCapacitor),
|
||||
__dirname: JSON.stringify(process.cwd()),
|
||||
__IS_MOBILE__: JSON.stringify(isCapacitor),
|
||||
__USE_QR_READER__: JSON.stringify(!isCapacitor),
|
||||
'process.platform': JSON.stringify('browser'),
|
||||
@@ -88,7 +87,7 @@ export async function createBuildConfig(mode: string): Promise<UserConfig> {
|
||||
'@nostr/tools',
|
||||
'@nostr/tools/nip06',
|
||||
],
|
||||
exclude: isElectron ? [
|
||||
exclude: isCapacitor ? [
|
||||
'register-service-worker',
|
||||
'workbox-window',
|
||||
'web-push',
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
import { defineConfig, mergeConfig } from "vite";
|
||||
import { createBuildConfig } from "./vite.config.common.mts";
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
const baseConfig = await createBuildConfig('electron');
|
||||
|
||||
return mergeConfig(baseConfig, {
|
||||
build: {
|
||||
outDir: 'dist-electron',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
// Main process entry points
|
||||
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
||||
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
||||
// Renderer process entry point (the web app)
|
||||
app: path.resolve(__dirname, 'index.html'),
|
||||
},
|
||||
external: ['electron'],
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: (chunkInfo) => {
|
||||
// Use different formats for main process vs renderer
|
||||
if (chunkInfo.name === 'main' || chunkInfo.name === 'preload') {
|
||||
return '[name].js';
|
||||
}
|
||||
return 'assets/[name].[hash].js';
|
||||
},
|
||||
assetFileNames: (assetInfo) => {
|
||||
// Keep main process files in root, others in assets
|
||||
if (assetInfo.name === 'main.js' || assetInfo.name === 'preload.js') {
|
||||
return '[name].[ext]';
|
||||
}
|
||||
return 'assets/[name].[hash].[ext]';
|
||||
},
|
||||
chunkFileNames: 'assets/[name].[hash].js',
|
||||
},
|
||||
},
|
||||
target: 'node18',
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['@/utils/logger']
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: 'typescript-transform',
|
||||
transform(code: string, id: string) {
|
||||
if (id.endsWith('main.ts')) {
|
||||
// Replace the logger import with inline logger
|
||||
return code.replace(
|
||||
/import\s*{\s*logger\s*}\s*from\s*['"]@\/utils\/logger['"];?/,
|
||||
`const logger = {
|
||||
log: (...args) => console.log(...args),
|
||||
error: (...args) => console.error(...args),
|
||||
info: (...args) => console.info(...args),
|
||||
warn: (...args) => console.warn(...args),
|
||||
debug: (...args) => console.debug(...args),
|
||||
};`
|
||||
);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'remove-sw-imports',
|
||||
transform(code: string, id: string) {
|
||||
// Remove service worker imports and registrations
|
||||
if (id.includes('registerServiceWorker') ||
|
||||
id.includes('register-service-worker') ||
|
||||
id.includes('sw_scripts') ||
|
||||
id.includes('PushNotificationPermission') ||
|
||||
code.includes('navigator.serviceWorker')) {
|
||||
return {
|
||||
code: code
|
||||
.replace(/import.*registerServiceWorker.*$/mg, '')
|
||||
.replace(/import.*register-service-worker.*$/mg, '')
|
||||
.replace(/navigator\.serviceWorker/g, 'undefined')
|
||||
.replace(/if\s*\([^)]*serviceWorker[^)]*\)\s*{[^}]*}/g, '')
|
||||
.replace(/import.*workbox.*$/mg, '')
|
||||
.replace(/importScripts\([^)]*\)/g, '')
|
||||
};
|
||||
}
|
||||
return code;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'remove-sw-files',
|
||||
enforce: 'pre',
|
||||
resolveId(id: string) {
|
||||
// Prevent service worker files from being included
|
||||
if (id.includes('sw.js') ||
|
||||
id.includes('workbox') ||
|
||||
id.includes('registerSW.js') ||
|
||||
id.includes('manifest.webmanifest')) {
|
||||
return '\0empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
load(id: string) {
|
||||
if (id === '\0empty') {
|
||||
return 'export default {}';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'electron-css-injection',
|
||||
enforce: 'post',
|
||||
generateBundle(options, bundle) {
|
||||
// Find the main CSS file
|
||||
const cssAsset = Object.values(bundle).find(
|
||||
(asset: any) => asset.type === 'asset' && asset.fileName?.endsWith('.css')
|
||||
) as any;
|
||||
|
||||
if (cssAsset) {
|
||||
// Find the HTML file and inject CSS link
|
||||
const htmlAsset = Object.values(bundle).find(
|
||||
(asset: any) => asset.type === 'asset' && asset.fileName?.endsWith('.html')
|
||||
) as any;
|
||||
|
||||
if (htmlAsset) {
|
||||
const cssHref = `./${cssAsset.fileName}`;
|
||||
const cssLink = ` <link rel="stylesheet" href="${cssHref}">\n`;
|
||||
|
||||
// Check if CSS link already exists
|
||||
if (!htmlAsset.source.includes(cssHref)) {
|
||||
// Inject CSS link after the title tag
|
||||
htmlAsset.source = htmlAsset.source.replace(
|
||||
/(<title>.*?<\/title>)/,
|
||||
`$1\n${cssLink}`
|
||||
);
|
||||
console.log(`[electron-css-injection] Injected CSS link: ${cssHref}`);
|
||||
} else {
|
||||
console.log(`[electron-css-injection] CSS link already present: ${cssHref}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
ssr: {
|
||||
noExternal: ['@/utils/logger']
|
||||
},
|
||||
base: './',
|
||||
publicDir: 'public',
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
base: './',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist-electron/www',
|
||||
emptyOutDir: false,
|
||||
assetsDir: 'assets',
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
input: path.resolve(__dirname, 'src/main.electron.ts'),
|
||||
output: {
|
||||
entryFileNames: 'main.electron.js',
|
||||
assetFileNames: 'assets/[name]-[hash][extname]',
|
||||
chunkFileNames: 'assets/[name]-[hash].js',
|
||||
manualChunks: undefined
|
||||
}
|
||||
},
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user