Something went wrong. Try again.
A local-first note taking app
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210/** * Habitat URI branded string. Uses at:// URIs instead of automerge: URLs. */import type * as Automerge from '@automerge/automerge';
export type HabitatUri = string & { __brand: 'HabitatUri' };
export function isHabitatUri(value: string): value is HabitatUri { return value.startsWith('at://');}
export type VaultType = 'local' | 'synced';
/** * Minimal engine-facing view of a vault. This is the input contract for * `VaultSyncEngine` — deliberately decoupled from how the app catalogs vaults * (ids, local paths, persistence). Derive it from a `Vault` via * `toVaultConfig` (see `vault/registry.ts`). */export interface VaultConfig { type: VaultType; rootUri: HabitatUri;}
/** * Collection types for vault documents. */export type VaultDocumentType = 'directory' | 'file';
/** * Entry in a directory document's `docs` array. * * `type` is `string` rather than `'file' | 'folder'` to stay compatible * with pushwork 2.x, which uses extension strings (`'md'`, `'png'`) for * file entries. Only `'folder'` is unambiguously a directory. */export interface DirectoryEntry { name: string; type: string; url: HabitatUri;}
/** * Directory node (in Automerge blob). Mirrors pushwork's * `PushworkDirectoryDocument` but with `HabitatUri` URLs. */export interface DirectoryDocument { '@patchwork': { type: 'folder' }; docs: DirectoryEntry[]; name?: string; title?: string; lastSyncAt?: number; with?: string;}
/** * File node (in Automerge blob). Mirrors pushwork's * `PushworkFileDocument` exactly for compatibility. */export interface FileDocument { '@patchwork': { type: 'file' }; name: string; extension: string; mimeType: string; content: string | Uint8Array; metadata: { permissions: number };}
/** * Union type for any vault document (file or directory). */export type VaultDocument = DirectoryDocument | FileDocument;
/** * True if a DirectoryEntry points at a sub-directory document. * False for any file leaf, regardless of how the producer encoded the * file's `type` (`'file'`, `'md'`, `'png'`, etc.). */export function isFolderEntry(entry: { type: unknown }): boolean { return entry.type === 'folder';}
/** * Convenience predicate: a file leaf. Requires `type` to actually be * a non-empty string — `undefined`, `null`, and `''` are NOT files. */export function isFileEntry(entry: { type: unknown }): boolean { return ( typeof entry.type === 'string' && entry.type.length > 0 && entry.type !== 'folder' );}
/** * Change type classification for sync operations. */export type ChangeType = 'add' | 'edit' | 'delete' | 'move';
/** * Represents a detected change during three-way comparison * (local filesystem ↔ snapshot ↔ remote doc). */export interface DetectedChange { type: ChangeType; path: string; isDirectory: boolean; oldPath?: string; newPath?: string; uri?: HabitatUri; head?: string[]; contentHash?: string;}
/** * Move detection result: a rename detected by content similarity. */export interface MoveCandidate { oldPath: string; newPath: string; similarity: number;}
/** * Tracked file entry in the sync snapshot. */export interface SnapshotFileEntry { uri: HabitatUri; head: string[]; contentHash: string;}
/** * Tracked directory entry in the sync snapshot. */export interface SnapshotDirectoryEntry { uri: HabitatUri; head: string[];}
/** * Union type for snapshot entries. */export type SnapshotEntry = SnapshotFileEntry | SnapshotDirectoryEntry;
/** * Sync snapshot for local state management. * Only used for synced vaults. */export interface SyncSnapshot { timestamp: number; rootUri: HabitatUri; files: Map<string, SnapshotFileEntry>; directories: Map<string, SnapshotDirectoryEntry>;}
/** * Serializable version of SyncSnapshot for JSON storage. */export interface SerializableSyncSnapshot { timestamp: number; rootUri: HabitatUri; files: Array<[string, SnapshotFileEntry]>; directories: Array<[string, SnapshotDirectoryEntry]>;}
/** * HabitatDocHandle mirrors the automerge-repo DocHandle API * but uses HabitatUri and is backed by AutomergeDocSession. * * This is a structural subset, not a literal implements. */export interface HabitatDocHandle<T> { /** The current document. */ doc(): Automerge.Doc<T>; /** Current document heads (like a git commit). */ heads(): string[]; /** Apply a change to the document. */ change(fn: (d: T) => void): void; /** Apply a change as if the document were at the given heads. */ changeAt(heads: string[], fn: (d: T) => void): void; /** View the document at a specific point in time. */ view(heads: string[]): Automerge.Doc<T>; /** Subscribe to local/remote changes. Returns an unsubscribe function. */ on(event: 'change', cb: () => void): () => void;}
/** * HabitatRepo mirrors the automerge-repo Repo API * but uses HabitatUri and is backed by Habitat PDS + AutomergeDocSession. * * Uses separate collections for directories and files: * - network.habitat.vault.directory * - network.habitat.vault.file */export interface HabitatRepo { /** Load an existing Habitat record into a handle. */ find<T>(uri: HabitatUri): Promise<HabitatDocHandle<T>>; /** Create a new directory document. */ createDirectory( initial: DirectoryDocument, ): Promise<{ uri: HabitatUri; handle: HabitatDocHandle<DirectoryDocument> }>; /** Create a new file document. */ createFile( initial: FileDocument, ): Promise<{ uri: HabitatUri; handle: HabitatDocHandle<FileDocument> }>; /** Remove a record. */ delete(uri: HabitatUri): Promise<void>;}