feat(ext): typed-item TS types mirroring relicario-core serde

This commit is contained in:
adlee-was-taken
2026-04-20 19:42:31 -04:00
parent 14aaac672c
commit 04c9503036

View File

@@ -1,32 +1,202 @@
/// Full credential entry (matches Rust Entry struct in relicario-core).
export interface Entry {
name: string;
url?: string;
/// Typed-item shared TypeScript types.
///
/// These mirror the Rust core's serde serialization. See
/// crates/relicario-core/src/item.rs, item_types/, and settings.rs
/// for the source shapes.
// --- IDs ---
export type ItemId = string; // 16-char hex
export type FieldId = string; // 16-char hex
export type AttachmentId = string; // 16-char hex (sha256 of plaintext, truncated)
// --- ItemType / ItemCore ---
// snake_case from serde rename_all
export type ItemType =
| 'login' | 'secure_note' | 'identity' | 'card' | 'key' | 'document' | 'totp';
// ItemCore is internally-tagged on "type":
// Login → { type: 'login', username, password, url, totp }
export type ItemCore =
| ({ type: 'login' } & LoginCore)
| ({ type: 'secure_note' } & SecureNoteCore)
| ({ type: 'identity' } & IdentityCore)
| ({ type: 'card' } & CardCore)
| ({ type: 'key' } & KeyCore)
| ({ type: 'document' } & DocumentCore)
| ({ type: 'totp' } & TotpCore);
// Optional fields use `?` because Rust #[serde(skip_serializing_if = "Option::is_none")]
// omits them from the JSON; serde_wasm_bindgen produces `undefined` on read.
export interface LoginCore {
username?: string;
password: string;
password?: string;
url?: string;
totp?: TotpConfig;
}
export interface SecureNoteCore { body: string; }
export interface IdentityCore {
full_name?: string;
address?: string;
phone?: string;
email?: string;
date_of_birth?: string; // "YYYY-MM-DD"
}
export interface CardCore {
number?: string;
holder?: string;
expiry?: { month: number; year: number };
cvv?: string;
pin?: string;
kind: CardKind;
}
export type CardKind = 'credit' | 'debit' | 'gift' | 'loyalty' | 'other';
export interface KeyCore {
key_material: string;
label?: string;
public_key?: string;
algorithm?: string;
}
export interface DocumentCore {
filename: string;
mime_type: string;
primary_attachment: AttachmentId;
}
export interface TotpCore {
config: TotpConfig;
issuer?: string;
label?: string;
}
// --- TOTP ---
export type TotpKind = 'totp' | 'steam' | { hotp: { counter: number } };
export interface TotpConfig {
secret: number[]; // Vec<u8> → JSON number array
algorithm: 'sha1' | 'sha256' | 'sha512';
digits: number;
period_seconds: number;
kind: TotpKind;
}
// --- Sections + custom fields ---
export interface Section {
name?: string;
fields: Field[];
}
export interface Field {
id: FieldId;
label: string;
kind: FieldKind;
value: FieldValue;
hidden_by_default: boolean;
}
export type FieldKind =
| 'text' | 'multiline' | 'password' | 'concealed' | 'url' | 'email'
| 'phone' | 'date' | 'month_year' | 'totp' | 'reference';
// adjacently-tagged { tag: "kind", content: "value" }
export type FieldValue =
| { kind: 'text'; value: string }
| { kind: 'multiline'; value: string }
| { kind: 'password'; value: string }
| { kind: 'concealed'; value: string }
| { kind: 'url'; value: string }
| { kind: 'email'; value: string }
| { kind: 'phone'; value: string }
| { kind: 'date'; value: string }
| { kind: 'month_year'; value: { month: number; year: number } }
| { kind: 'totp'; value: TotpConfig }
| { kind: 'reference'; value: AttachmentId };
// --- Attachments + history ---
export interface AttachmentRef {
id: AttachmentId;
filename: string;
mime_type: string;
size: number;
created: number;
}
export interface FieldHistoryEntry {
value: string;
replaced_at: number;
}
export interface AttachmentSummary {
id: AttachmentId;
filename: string;
mime_type: string;
size: number;
}
// --- Item envelope ---
export interface Item {
id: ItemId;
title: string;
type: ItemType; // Rust r#type → JSON key "type"
tags: string[];
favorite: boolean;
group?: string;
notes?: string;
totp_secret?: string;
group?: string;
created_at: string;
updated_at: string;
created: number;
modified: number;
trashed_at?: number;
core: ItemCore;
sections: Section[];
attachments: AttachmentRef[];
field_history: Record<FieldId, FieldHistoryEntry[]>;
}
/// Lightweight manifest entry for listing/searching without full decrypt.
export interface ManifestEntry {
name: string;
url?: string;
username?: string;
group?: string;
updated_at: string;
}
// --- Manifest (schema_version 2) ---
/// Encrypted manifest containing all entry metadata.
export interface Manifest {
entries: Record<string, ManifestEntry>;
version: number;
schema_version: number; // 2
items: Record<ItemId, ManifestEntry>;
}
/// Configuration for connecting to a git host.
export interface ManifestEntry {
id: ItemId;
type: ItemType;
title: string;
tags: string[];
favorite: boolean;
group?: string;
icon_hint?: string;
modified: number;
trashed_at?: number;
attachment_summaries: AttachmentSummary[];
}
// --- Vault settings (only the fields α touches) ---
// Full shape lives on the Rust side and in docs/superpowers/specs/2026-04-18-relicario-typed-items-design.md
// We leave retention/generator/caps opaque to α so we don't accidentally mutate them.
export interface VaultSettings {
trash_retention: unknown;
field_history_retention: unknown;
generator_defaults: unknown;
attachment_caps: unknown;
autofill_origin_acks: Record<string, number>;
}
// --- Vault config (device-local) ---
export interface VaultConfig {
hostType: 'gitea' | 'github';
hostUrl: string;
@@ -34,20 +204,41 @@ export interface VaultConfig {
apiToken: string;
}
/// Persisted setup state in chrome.storage.local.
export interface SetupState {
config: VaultConfig | null;
imageBase64: string | null;
isConfigured: boolean;
}
/// User-configurable credential capture settings.
export interface RelicarioSettings {
// --- Device-local UX settings (chrome.storage.local — renamed from RelicarioSettings) ---
export interface DeviceSettings {
captureEnabled: boolean;
captureStyle: 'bar' | 'toast';
}
export const DEFAULT_SETTINGS: RelicarioSettings = {
export const DEFAULT_DEVICE_SETTINGS: DeviceSettings = {
captureEnabled: false,
captureStyle: 'bar',
};
// --- Generator request (matches Rust GeneratorRequest — tag="kind") ---
export type GeneratorRequest =
| { kind: 'bip39'; word_count: number; separator: string; capitalization: Capitalization }
| { kind: 'random'; length: number; classes: CharClasses; symbol_charset: SymbolCharset };
export type Capitalization = 'lower' | 'upper' | 'first_of_each' | 'title' | 'mixed';
export interface CharClasses { lower: boolean; upper: boolean; digits: boolean; symbols: boolean; }
export type SymbolCharset =
| { kind: 'safe_only' }
| { kind: 'extended' }
| { kind: 'custom'; value: string };
// Default used by the α popup "gen" button:
export const DEFAULT_PASSWORD_REQUEST: GeneratorRequest = {
kind: 'random',
length: 20,
classes: { lower: true, upper: true, digits: true, symbols: true },
symbol_charset: { kind: 'safe_only' },
};