From 8c0b547855758270aaed7e3978bc7fdba729df27 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 12 Aug 2025 09:12:46 +0000 Subject: [PATCH 1/6] fix(typescript): resolve production build errors and add ESLint ignore comments - Fix TypeScript compilation errors in deepLinks service by replacing logConsoleAndDb with logger.error - Add ESLint disable comments for necessary 'any' type usage in worker polyfills and Vue mixins - Add ESLint disable comments for console statements in test files and debugging code - Production build now succeeds with npm run build:web:prod - TypeScript compilation passes with npm run type-check The deepLinks service was using undefined logConsoleAndDb function causing build failures. Worker context polyfills and Vue mixin complexity require 'any' type usage in specific cases. Console statements in test files and debugging code are intentionally used for development. --- src/services/AbsurdSqlDatabaseService.ts | 2 ++ src/services/deepLinks.ts | 10 ++++------ src/test/PlatformServiceMixinTest.vue | 2 ++ src/utils/PlatformServiceMixin.ts | 5 +++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/services/AbsurdSqlDatabaseService.ts b/src/services/AbsurdSqlDatabaseService.ts index 707513e5..5d2ab7ad 100644 --- a/src/services/AbsurdSqlDatabaseService.ts +++ b/src/services/AbsurdSqlDatabaseService.ts @@ -1,7 +1,9 @@ // **WORKER-COMPATIBLE CRYPTO POLYFILL**: Must be at the very top // This prevents "crypto is not defined" errors when running in worker context if (typeof window === "undefined" && typeof crypto === "undefined") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).crypto = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any getRandomValues: (array: any) => { // Simple fallback for worker context for (let i = 0; i < array.length; i++) { diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index ad97ef46..62ae7b15 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -53,6 +53,7 @@ import { DeepLinkRoute, } from "../interfaces/deepLinks"; import type { DeepLinkError } from "../interfaces/deepLinks"; +import { logger } from "../utils/logger"; // Helper function to extract the first key from a Zod object schema function getFirstKeyFromZodObject( @@ -204,9 +205,8 @@ export class DeepLinkHandler { validatedParams = await schema.parseAsync(params); } catch (error) { // For parameter validation errors, provide specific error feedback - logConsoleAndDb( + logger.error( `[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, - true, ); await this.router.replace({ name: "deep-link-error", @@ -229,9 +229,8 @@ export class DeepLinkHandler { params: validatedParams, }); } catch (error) { - logConsoleAndDb( + logger.error( `[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)}`, - true, ); // For parameter validation errors, provide specific error feedback await this.router.replace({ @@ -263,9 +262,8 @@ export class DeepLinkHandler { await this.validateAndRoute(path, sanitizedParams, query); } catch (error) { const deepLinkError = error as DeepLinkError; - logConsoleAndDb( + logger.error( `[DeepLink] Error (${deepLinkError.code}): ${deepLinkError.details}`, - true, ); throw { diff --git a/src/test/PlatformServiceMixinTest.vue b/src/test/PlatformServiceMixinTest.vue index f54eea11..219c72cf 100644 --- a/src/test/PlatformServiceMixinTest.vue +++ b/src/test/PlatformServiceMixinTest.vue @@ -92,6 +92,7 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; }) export default class PlatformServiceMixinTest extends Vue { result: string = ""; + // eslint-disable-next-line @typescript-eslint/no-explicit-any userZeroTestResult: any = null; activeTest: string = ""; // Track which test is currently active @@ -267,6 +268,7 @@ This tests the complete save → retrieve cycle with actual database interaction this.result = `User #0 settings test completed. isRegistered: ${accountSettings.isRegistered}`; } catch (error) { this.result = `Error testing User #0 settings: ${error}`; + // eslint-disable-next-line no-console console.error("Error testing User #0 settings:", error); } } diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 361a2ff2..68c09720 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -133,6 +133,7 @@ export const PlatformServiceMixin = { * Used for change detection and component updates */ currentActiveDid(): string | null { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return (this as any)._currentActiveDid; }, @@ -200,7 +201,9 @@ export const PlatformServiceMixin = { * This method should be called when the user switches identities */ async $updateActiveDid(newDid: string | null): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const oldDid = (this as any)._currentActiveDid; + // eslint-disable-next-line @typescript-eslint/no-explicit-any (this as any)._currentActiveDid = newDid; if (newDid !== oldDid) { @@ -291,6 +294,7 @@ export const PlatformServiceMixin = { // Convert searchBoxes array to JSON string if present if (settings.searchBoxes !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any (converted as any).searchBoxes = Array.isArray(settings.searchBoxes) ? JSON.stringify(settings.searchBoxes) : String(settings.searchBoxes); @@ -692,6 +696,7 @@ export const PlatformServiceMixin = { typeof method.value === "string"; if (!isValid && method !== undefined) { + // eslint-disable-next-line no-console console.warn( "[ContactNormalization] Invalid contact method:", method, From adfaef7947b0a48bf7fd1f0e7df61df73977cec3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 13 Aug 2025 10:53:10 +0000 Subject: [PATCH 2/6] fix(lint): resolve low and medium impact warnings - Remove console statements and replace with proper logging - Fix line length violations in comments - Maintain functionality while improving code quality Reduces lint warnings from 30 to 25 by addressing: - 3 console statement violations - 2 line length violations --- src/components/ContactInputForm.vue | 2 +- src/services/deepLinks.ts | 2 +- src/services/platforms/WebPlatformService.ts | 3 ++- src/views/AccountViewView.vue | 3 ++- src/views/DIDView.vue | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/ContactInputForm.vue b/src/components/ContactInputForm.vue index 35c693e4..dbbc1485 100644 --- a/src/components/ContactInputForm.vue +++ b/src/components/ContactInputForm.vue @@ -167,7 +167,7 @@ export default class ContactInputForm extends Vue { */ @Emit("qr-scan") private handleQRScan(): void { - console.log("[ContactInputForm] QR scan button clicked"); + // QR scan button clicked - event emitted for parent handling } } diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index 62ae7b15..d8445607 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -179,7 +179,7 @@ export class DeepLinkHandler { const validRoute = routeSchema.parse(path) as DeepLinkRoute; routeName = ROUTE_MAP[validRoute].name; } catch (error) { - console.error(`[DeepLink] Invalid route path: ${path}`); + logger.error(`[DeepLink] Invalid route path: ${path}`); // Redirect to error page with information about the invalid link await this.router.replace({ diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 7ec13c01..a731ee22 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -693,7 +693,8 @@ export class WebPlatformService implements PlatformService { const setClause = keys.map((key) => `${key} = ?`).join(", "); const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`; const params = [...keys.map((key) => settings[key]), did]; - console.log( + // Log update operation for debugging + logger.debug( "[WebPlatformService] updateDidSpecificSettings", sql, JSON.stringify(params, null, 2), diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 892e1ebc..81c2978e 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -61,7 +61,8 @@ /> - +

- + Date: Wed, 13 Aug 2025 09:05:04 -0600 Subject: [PATCH 3/6] doc: Add helpful setup instructions for quick start for new devs. --- BUILDING.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index ba5649da..e1e94fcd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -8,8 +8,10 @@ This guide explains how to build TimeSafari for different platforms using the co ```bash # 🖥️ Web Development -npm run build:web:dev # Start development server with hot reload -npm run build:web:prod # Production build +npm install # setup -- and pkgx.dev `dev` command before this will set environment with npm, etc +npm run build:web:serve -- --test # Start with test endorser server +npm run build:web:dev # Start development server with hot reload with local endorser server +npm run build:web:prod # Production build # 📱 Mobile Development npm run build:ios # iOS build (opens Xcode) @@ -2401,4 +2403,4 @@ All scripts use consistent error handling: --- -**Note**: This documentation is maintained alongside the build system. For the most up-to-date information, refer to the actual script files and Vite configuration files in the repository. \ No newline at end of file +**Note**: This documentation is maintained alongside the build system. For the most up-to-date information, refer to the actual script files and Vite configuration files in the repository. From 24a2dce43e32ef41d849d084acb62551117eaa24 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 11:17:08 -0600 Subject: [PATCH 4/6] chore: Bump version and add "-beta". --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d19bd08..5b40d345 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", diff --git a/package.json b/package.json index cd34bc17..53e3f5b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timesafari", - "version": "1.0.6", + "version": "1.0.7-beta", "description": "Time Safari Application", "author": { "name": "Time Safari Team" From a221a5c5ed4a385c13bd9db68f207f2472a29166 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 12:18:06 -0600 Subject: [PATCH 5/6] feat: Add easier way for test users to register themselves. --- src/libs/endorserServer.ts | 10 +++--- src/router/index.ts | 18 +++++----- src/test/index.ts | 32 ++++++++++++++---- src/views/TestView.vue | 67 ++++++++++++++++++++++++++++++-------- 4 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index e28b3ac9..735252f7 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -1348,12 +1348,12 @@ export async function createEndorserJwtVcFromClaim( } /** - * Create a JWT for a RegisterAction claim. + * Create a JWT for a RegisterAction claim, used for registrations & invites. * * @param activeDid - The DID of the user creating the invite - * @param contact - The contact to register, with a 'did' field (all optional for invites) - * @param identifier - The identifier for the invite, usually random - * @param expiresIn - The number of seconds until the invite expires + * @param contact - Optional - The contact to register, with a 'did' field (all optional for invites) + * @param identifier - Optional - The identifier for the invite, usually random + * @param expiresIn - Optional - The number of seconds until the invite expires * @returns The JWT for the RegisterAction claim */ export async function createInviteJwt( @@ -1367,7 +1367,7 @@ export async function createInviteJwt( "@type": "RegisterAction", agent: { identifier: activeDid }, object: SERVICE_ID, - identifier: identifier, + identifier: identifier, // not sent if undefined }; if (contact?.did) { vcClaim.participant = { identifier: contact.did }; diff --git a/src/router/index.ts b/src/router/index.ts index 7d1e738c..043d3d0c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -82,6 +82,15 @@ const routes: Array = [ name: "database-migration", component: () => import("../views/DatabaseMigration.vue"), }, + { + path: "/deep-link-error", + name: "deep-link-error", + component: () => import("../views/DeepLinkErrorView.vue"), + meta: { + title: "Invalid Deep Link", + requiresAuth: false, + }, + }, { path: "/did/:did?", name: "did", @@ -276,15 +285,6 @@ const routes: Array = [ name: "user-profile", component: () => import("../views/UserProfileView.vue"), }, - { - path: "/deep-link-error", - name: "deep-link-error", - component: () => import("../views/DeepLinkErrorView.vue"), - meta: { - title: "Invalid Deep Link", - requiresAuth: false, - }, - }, ]; const isElectron = window.location.protocol === "file:"; diff --git a/src/test/index.ts b/src/test/index.ts index 28b0e349..8974713b 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1,9 +1,29 @@ import axios from "axios"; import * as didJwt from "did-jwt"; import { SERVICE_ID } from "../libs/endorserServer"; -import { deriveAddress, newIdentifier } from "../libs/crypto"; +import { + DEFAULT_ROOT_DERIVATION_PATH, + deriveAddress, + newIdentifier, +} from "../libs/crypto"; import { logger } from "../utils/logger"; import { AppString } from "../constants/app"; +import { saveNewIdentity } from "@/libs/util"; +import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; + +const TEST_USER_0_MNEMONIC = + "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage"; + +export async function testBecomeUser0() { + const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); + + const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); + await saveNewIdentity(identity0, TEST_USER_0_MNEMONIC, DEFAULT_ROOT_DERIVATION_PATH); + const platformService = await PlatformServiceFactory.getInstance(); + await platformService.updateDidSpecificSettings(identity0.did, { + isRegistered: true, + }); +} /** * Get User #0 to sign & submit a RegisterAction for the user's activeDid. @@ -15,10 +35,7 @@ import { AppString } from "../constants/app"; * @throws Error if registration fails or database access fails */ export async function testServerRegisterUser() { - const testUser0Mnem = - "seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control"; - - const [addr, privateHex, publicHex, deriPath] = deriveAddress(testUser0Mnem); + const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); @@ -32,9 +49,9 @@ export async function testServerRegisterUser() { const vcClaim = { "@context": "https://schema.org", "@type": "RegisterAction", - agent: { did: identity0.did }, + agent: { identifier: identity0.did }, object: SERVICE_ID, - participant: { did: settings.activeDid }, + participant: { identifier: settings.activeDid }, }; // Make a payload for the claim @@ -71,4 +88,5 @@ export async function testServerRegisterUser() { const resp = await axios.post(url, payload, { headers }); logger.log("User registration result:", resp); + return resp; } diff --git a/src/views/TestView.vue b/src/views/TestView.vue index a7592afa..df879ad2 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -21,7 +21,17 @@

-
+
+

User Registration

+ + +
+ +

Notiwind Alerts

@@ -99,7 +109,7 @@
Register Passkey -
@@ -235,6 +245,7 @@ import { registerAndSavePasskey, SHARED_PHOTO_BASE64_KEY, } from "../libs/util"; +import { testBecomeUser0, testServerRegisterUser } from "@/test"; import { logger } from "../utils/logger"; import { Account } from "../db/tables/accounts"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; @@ -300,6 +311,7 @@ export default class Help extends Vue { // for passkeys credIdHex?: string; activeDid?: string; + apiServer?: string; jwt?: string; peerSetup?: PeerSetup; userName?: string; @@ -521,17 +533,6 @@ export default class Help extends Vue { ]; } - /** - * Method to trigger notification test - * Centralizes notification testing logic - */ - triggerTestNotification(config: { - notification: NotificationIface; - timeout?: number; - }) { - this.$notify(config.notification, config.timeout); - } - /** * Component initialization * @@ -541,6 +542,7 @@ export default class Help extends Vue { async mounted() { const settings = await this.$accountSettings(); this.activeDid = settings.activeDid || ""; + this.apiServer = settings.apiServer || ""; this.userName = settings.firstName; const account = await retrieveAccountMetadata(this.activeDid); @@ -553,6 +555,43 @@ export default class Help extends Vue { } } + /** + * Checks if running on production server + * + * @returns True if not on production server (enables test utilities) + */ + public isNotProdServer() { + return this.apiServer !== AppString.PROD_ENDORSER_API_SERVER; + } + + async registerMe() { + const response = await testServerRegisterUser(); + if (response.status === 201) { + alert("Registration successful."); + this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change + } else { + logger.error("Registration failure response:", response); + alert("Registration failed: " + (response.data.error || response.data)); + } + } + + async becomeUser0() { + await testBecomeUser0(); + alert("You are now User 0."); + this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change + } + + /** + * Method to trigger notification test + * Centralizes notification testing logic + */ + triggerTestNotification(config: { + notification: NotificationIface; + timeout?: number; + }) { + this.$notify(config.notification, config.timeout); + } + /** * Handles file upload for image sharing tests * @@ -609,7 +648,7 @@ export default class Help extends Vue { * Includes validation and user confirmation workflow * Uses notification helpers for consistent messaging */ - public async register() { + public async registerPasskey() { const DEFAULT_USERNAME = AppString.APP_NAME + " Tester"; if (!this.userName) { const modalConfig = createPasskeyNameModal( From 81096a8bee0a2be936e8e90fae5e30efb401b73f Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 13 Aug 2025 18:59:00 -0600 Subject: [PATCH 6/6] doc: Add instructions to become test user, and other README refactors. --- README.md | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 6d987467..59290d4d 100644 --- a/README.md +++ b/README.md @@ -3,36 +3,9 @@ [Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude and expand to crowd-fund with time & money, then record and see the impact of contributions. -## Database Migration Status - -**Current Status**: The application is undergoing a migration from Dexie (IndexedDB) to SQLite using absurd-sql. This migration is in **Phase 2** with a well-defined migration fence in place. - -### Migration Progress -- ✅ **SQLite Database Service**: Fully implemented with absurd-sql -- ✅ **Platform Service Layer**: Unified database interface across platforms -- ✅ **Settings Migration**: Core user settings transferred -- ✅ **Account Migration**: Identity and key management -- 🔄 **Contact Migration**: User contact data (via import interface) -- 📋 **Code Cleanup**: Remove unused Dexie imports - -### Migration Fence -The migration is controlled by a **migration fence** that separates legacy Dexie code from the new SQLite implementation. See [Migration Fence Definition](doc/migration-fence-definition.md) for complete details. - -**Key Points**: -- Legacy Dexie database is disabled by default -- All database operations go through `PlatformServiceMixin` -- Migration tools provide controlled access to both databases -- Clear separation between legacy and new code - -### Migration Documentation -- [Migration Guide](doc/migration-to-wa-sqlite.md) - Complete migration process -- [Migration Fence Definition](doc/migration-fence-definition.md) - Fence boundaries and rules -- [Database Migration Guide](doc/database-migration-guide.md) - User-facing migration tools - ## Roadmap -See [project.task.yaml](project.task.yaml) for current priorities. -(Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.) +See [ClickUp](https://sharing.clickup.com/9014278710/l/h/8cmnyhp-174/10573fec74e2ba0) for current priorities. ## Setup & Building @@ -42,14 +15,16 @@ Quick start: ```bash npm install -npm run dev +npm run build:web:serve -- --test ``` +To be able to make submissions: go to "profile" (bottom left), go to the bottom and expand "Show Advanced Settings", go to the bottom and to the "Test Page", and finally "Become User 0" to see all the functionality. + See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker). ## Development Database Clearing -TimeSafari provides a simple script-based approach to clear the database for development purposes. +TimeSafari provides a simple script-based approach to clear the local database (not the claim server) for development purposes. ### Quick Usage ```bash @@ -126,7 +101,6 @@ const apiUrl = `${APP_SERVER}/api/claim/123`; ### Documentation -- [Domain Configuration System](docs/domain-configuration.md) - Complete guide - [Constants and Configuration](src/constants/app.ts) - Core constants ## Tests