feat: add mobile vault config model

This commit is contained in:
lucaronin
2026-05-04 09:41:06 +02:00
parent 601306afb5
commit 165ec6f2b0
3 changed files with 190 additions and 3 deletions

View File

@@ -0,0 +1,82 @@
import { describe, expect, it } from 'vitest'
import { createMobileVaultConfig } from './mobileVaultConfig'
describe('mobile vault config', () => {
it('creates an app-local vault without sync when no remote is provided', () => {
expect(createMobileVaultConfig({ id: 'personal', name: 'Personal Journal' })).toEqual({
ok: true,
config: {
id: 'personal',
name: 'Personal Journal',
storage: {
kind: 'appLocalGit',
directoryName: 'personal-journal',
},
sync: {
state: 'localOnly',
remote: null,
authRequirement: null,
},
},
})
})
it('uses GitHub OAuth for GitHub-backed vaults', () => {
const result = createMobileVaultConfig({
id: 'tolaria',
name: 'Tolaria MVP',
remoteUrl: 'https://github.com/refactoringhq/tolaria.git',
})
expect(result).toMatchObject({
ok: true,
config: {
sync: {
state: 'remoteReady',
remote: {
host: 'github.com',
owner: 'refactoringhq',
repository: 'tolaria',
},
authRequirement: {
strategy: 'githubOAuth',
host: 'github.com',
},
},
},
})
})
it('requires SSH keys for arbitrary Git remotes', () => {
const result = createMobileVaultConfig({
id: 'work',
name: 'Work',
remoteUrl: 'ssh://git@git.example.com/acme/notes.git',
})
expect(result).toMatchObject({
ok: true,
config: {
sync: {
authRequirement: {
strategy: 'sshKey',
host: 'git.example.com',
},
},
},
})
})
it('rejects invalid remote URLs before persisting a config', () => {
expect(
createMobileVaultConfig({
id: 'broken',
name: 'Broken',
remoteUrl: '/Users/luca/Laputa',
}),
).toEqual({
ok: false,
error: 'invalidRemoteUrl',
})
})
})

View File

@@ -0,0 +1,100 @@
import { parseMobileGitRemote, type MobileGitAuthStrategy, type MobileGitRemote } from './mobileGitRemote'
export type MobileVaultStorage = {
kind: 'appLocalGit'
directoryName: string
}
export type MobileVaultAuthRequirement = {
strategy: MobileGitAuthStrategy
host: string
}
export type MobileVaultSync =
| {
state: 'localOnly'
remote: null
authRequirement: null
}
| {
state: 'remoteReady'
remote: MobileGitRemote
authRequirement: MobileVaultAuthRequirement
}
export type MobileVaultConfig = {
id: string
name: string
storage: MobileVaultStorage
sync: MobileVaultSync
}
export type CreateMobileVaultConfigInput = {
id: string
name: string
remoteUrl?: string
}
export type MobileVaultConfigError = 'invalidRemoteUrl'
export type CreateMobileVaultConfigResult =
| {
ok: true
config: MobileVaultConfig
}
| {
ok: false
error: MobileVaultConfigError
}
export function createMobileVaultConfig(input: CreateMobileVaultConfigInput): CreateMobileVaultConfigResult {
const sync = createSyncConfig(input.remoteUrl)
if (!sync) {
return { ok: false, error: 'invalidRemoteUrl' }
}
return {
ok: true,
config: {
id: input.id,
name: input.name,
storage: createStorageConfig(input.name),
sync,
},
}
}
function createSyncConfig(remoteUrl: string | undefined): MobileVaultSync | null {
if (!remoteUrl?.trim()) {
return { state: 'localOnly', remote: null, authRequirement: null }
}
const remote = parseMobileGitRemote(remoteUrl)
return remote ? createRemoteSync(remote) : null
}
function createRemoteSync(remote: MobileGitRemote): MobileVaultSync {
return {
state: 'remoteReady',
remote,
authRequirement: {
strategy: remote.authStrategy,
host: remote.host,
},
}
}
function createStorageConfig(name: string): MobileVaultStorage {
return {
kind: 'appLocalGit',
directoryName: createDirectoryName(name),
}
}
function createDirectoryName(name: string) {
return name
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
}

View File

@@ -8,7 +8,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Branch: `codex/mobile`
- Active phase: Phase 2 - Mobile Shell
- Active slice: Add mobile Git remote auth strategy parsing
- Active slice: Connect parsed Git remotes to mobile vault configuration
- Push policy: commit locally; do not push unless explicitly requested
- Validation target: iPad/iOS simulator first
@@ -44,14 +44,15 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Added a tested compact gesture mapper and `SwipeSurface` wrapper so phone panels can transition through swipe gestures while keeping panel behavior in the reducer.
- Re-tested the iPad simulator path; the app now launches in Expo Go on `iPad Pro 13-inch (M4)`.
- Added a pure mobile Git remote parser that codifies the auth choice: GitHub remotes use the GitHub OAuth App path, arbitrary Git remotes use the SSH-key path.
- Added a pure mobile vault configuration model that keeps vault storage app-local, distinguishes local-only vs remote-backed sync, and derives the required Git auth path from the parsed remote.
## Next Action
Continue Phase 2 with the next mobile shell slice:
1. Dismiss or suppress Expo Go's first-run tools modal during simulator QA so screenshots capture the app without the overlay.
2. Connect parsed Git remotes to a mobile vault configuration model.
3. Begin the TenTap editor spike behind a `MobileEditorAdapter` once the shell/storage boundary is stable.
2. Begin the TenTap editor spike behind a `MobileEditorAdapter` once the shell/storage boundary is stable.
3. Add the first native storage adapter around the vault config/repository contracts after the editor spike confirms the app shape.
## Verification Log
@@ -119,6 +120,10 @@ Continue Phase 2 with the next mobile shell slice:
- `pnpm --filter @tolaria/mobile typecheck` passed after Git remote auth parsing.
- CodeScene after Git remote auth parsing: `apps/mobile/src/mobileGitRemote.ts` and `apps/mobile/src/mobileGitRemote.test.ts` scored `10`.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after Git remote auth parsing.
- `pnpm --filter @tolaria/mobile test -- src/mobileVaultConfig.test.ts` passed after vault config extraction: 7 files / 24 tests.
- `pnpm --filter @tolaria/mobile typecheck` passed after vault config extraction.
- CodeScene after vault config extraction: `apps/mobile/src/mobileVaultConfig.ts` and `apps/mobile/src/mobileVaultConfig.test.ts` scored `10`.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after vault config extraction.
## Risks / Watch Items