@majikah/majik-signature-client 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +110 -0
  2. package/dist/core/backup/constants.d.ts +3 -3
  3. package/dist/core/client-state-manager.d.ts +3 -77
  4. package/dist/core/client-state-manager.js +3 -147
  5. package/dist/core/contacts/majik-contact-directory.d.ts +3 -2
  6. package/dist/core/contacts/majik-contact-directory.js +8 -8
  7. package/dist/core/contacts/majik-contact-manager.d.ts +3 -2
  8. package/dist/core/contacts/majik-contact-manager.js +5 -5
  9. package/dist/core/storage/client-state/_types.d.ts +1 -8
  10. package/dist/core/storage/client-state/_types.js +2 -1
  11. package/dist/core/storage/client-state/adapter-idb.d.ts +1 -1
  12. package/dist/core/storage/client-state/adapter-memory.d.ts +2 -1
  13. package/dist/core/storage/client-state/adapter-sql.d.ts +2 -1
  14. package/dist/core/storage/client-state/adapter-sql.js +8 -8
  15. package/dist/core/storage/contact-directory/contacts/adapter-sql.js +4 -4
  16. package/dist/core/storage/contact-directory/groups/adapter-sql.js +4 -4
  17. package/dist/core/storage/keystore/adapter-sql.js +4 -4
  18. package/dist/core/storage/sql-schema.js +67 -67
  19. package/dist/core/storage/stamps/adapter-sql.js +4 -4
  20. package/dist/core/types.d.ts +0 -4
  21. package/dist/majik-signature-client.d.ts +28 -427
  22. package/dist/majik-signature-client.js +67 -750
  23. package/package.json +3 -2
  24. package/dist/core/contacts/majik-contact.d.ts +0 -89
  25. package/dist/core/contacts/majik-contact.js +0 -214
  26. package/dist/core/crypto/keystore.d.ts +0 -228
  27. package/dist/core/crypto/keystore.js +0 -575
  28. package/dist/core/utils/APITranscoder.d.ts +0 -114
  29. package/dist/core/utils/APITranscoder.js +0 -305
  30. package/dist/core/utils/idb-majik-system.d.ts +0 -14
  31. package/dist/core/utils/idb-majik-system.js +0 -44
  32. package/dist/core/utils/majik-file-utils.d.ts +0 -16
  33. package/dist/core/utils/majik-file-utils.js +0 -153
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # Majik Signature Client
2
+
3
+ **Majik Signature Client** is a high-level TypeScript wrapper for `MajikSignature`, built directly on top of `MajikKeyClient`.
4
+
5
+ Designed to be used natively within the Majikah ecosystem, it inherits all core `MajikKey` account management—such as creation, lock/unlock states, and active-account tracking—while introducing domain-specific functionality for file signing, contact management, and encrypted reusable stamps.
6
+
7
+ ## Core Capabilities
8
+
9
+ * **Integrated Key & Account Management**: Extends `MajikKeyClient` to seamlessly handle account lifecycles and cryptographic unlocking. Accounts can easily be shared alongside `MajikMessage`.
10
+ * **Contact & Group Directory**: Utilizes `MajikContactManager` for full contact CRUD operations, including system/user groups, favorites, and blocked lists.
11
+ * **Post-Quantum Signing & Verification**: Provides intuitive methods (`signFile`, `verifyFile`, `batchSignFiles`) for text, raw bytes, and file blobs. Verifications automatically resolve public keys from the internal contact directory or active keystore.
12
+ * **Multi-sig & Envelope Sealing**: Built-in support for restricted allowlists (`expectedSigners`), multi-sig pending states, and cryptographic sealing to prevent further modifications to a file.
13
+ * **Encrypted Stamp Assets**: Uses `MajikSignatureStampManager` to securely create, encrypt, and manage reusable assets (signatures, audio, text) bound to the user's ML-KEM keys.
14
+ * **Data Backup & Restore**: Robust export and hydration capabilities for contacts, stamps, and user preferences utilizing `MajikCompressedJSON` and protected by strict magic byte headers.
15
+
16
+ ---
17
+
18
+ ## Initialization
19
+
20
+ You can instantiate and fully hydrate the client and its storage adapters in a single call using the static `create` method.
21
+
22
+ ```typescript
23
+ import { MajikSignatureClient } from "your-path-here"; // Update with actual package export
24
+
25
+ // 1. Initialize and hydrate the client state, keys, contacts, and stamps
26
+ const client = await MajikSignatureClient.create({
27
+ adapters: {
28
+ // Inject your target storage adapters here (e.g., IndexedDB)
29
+ contacts: myContactsAdapter,
30
+ stamps: myStampsAdapter
31
+ }
32
+ });
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Quick Usage API
38
+
39
+ ### 1. File Signing & Sealing
40
+
41
+ Sign a file using the currently active unlocked account, then restrict future signers and seal it.
42
+
43
+ ```typescript
44
+ // Sign a file blob
45
+ const { blob: signedBlob, signature } = await client.signFile(myFile, {
46
+ contentType: "application/pdf"
47
+ });
48
+
49
+ // Optionally seal a multi-sig file to lock the envelope
50
+ const { sealInfo } = await client.seal(signedBlob);
51
+ console.log("Sealed at:", sealInfo.sealTimestamp);
52
+ ```
53
+
54
+ ### 2. File Verification
55
+
56
+ Verify a file's embedded signature automatically using your contact directory to resolve the signer's identity.
57
+
58
+ ```typescript
59
+ // Verifies against known contacts or extracts self-reported keys
60
+ const result = await client.verifyFile(signedBlob, {
61
+ contactId: "expected_contact_id"
62
+ });
63
+
64
+ if (result.valid) {
65
+ console.log(`Authentic. Signed by: ${result.signerLabel || result.signerId}`);
66
+ }
67
+ ```
68
+
69
+ ### 3. Contact Management
70
+
71
+ Add external contacts and organize them into groups for streamlined verification.
72
+
73
+ ```typescript
74
+ // Import a contact from a compressed base64 string
75
+ const contact = await client.importContactCompressed(base64ContactString);
76
+
77
+ // Add the contact to a custom group
78
+ const myGroup = await client.createGroup("group_1", "Trusted Signers");
79
+ await client.addContactToGroup(myGroup.id, contact.id);
80
+ ```
81
+
82
+ ### 4. Encrypted Stamps
83
+
84
+ Create an encrypted asset (like an image signature) that is safely stored at rest and decrypted on the fly using ML-KEM.
85
+
86
+ ```typescript
87
+ // Create a new image stamp
88
+ const stamp = await client.createStamp(
89
+ imageBytes,
90
+ "image",
91
+ "My Primary Signature",
92
+ { mimeType: "image/png" }
93
+ );
94
+
95
+ // Decrypt content later for injection into a document
96
+ const decryptedBytes = await client.decryptStampContent(stamp.id);
97
+ ```
98
+
99
+ ### 5. App Data Backups
100
+
101
+ Export or restore the entire application state—including contacts, stamps, and preferences—into a `.MJKB` portable blob.
102
+
103
+ ```typescript
104
+ // Export
105
+ const backupBlob = await client.backupAppData();
106
+
107
+ // Restore
108
+ const restoreResults = await client.restoreAppData(backupBlob);
109
+ console.log(`Restored ${restoreResults.contacts} contacts.`);
110
+ ```
@@ -7,9 +7,9 @@
7
7
  * MJKA = MajiK App-data
8
8
  */
9
9
  export declare const MAJIK_SIGNATURE_BACKUP_MAGIC: {
10
- readonly stamps: Uint8Array;
11
- readonly contacts: Uint8Array;
12
- readonly appData: Uint8Array;
10
+ readonly stamps: Uint8Array<ArrayBuffer>;
11
+ readonly contacts: Uint8Array<ArrayBuffer>;
12
+ readonly appData: Uint8Array<ArrayBuffer>;
13
13
  };
14
14
  /** The union of valid magic-byte headers. */
15
15
  export type MajikMessageBackupMagic = (typeof MAJIK_SIGNATURE_BACKUP_MAGIC)[keyof typeof MAJIK_SIGNATURE_BACKUP_MAGIC];
@@ -24,84 +24,10 @@
24
24
  * hydrate() pass. Callers should never need to hydrate() again unless the
25
25
  * adapter is swapped.
26
26
  */
27
- import { AccountOrderValue, ClientStateEntry, ClientStateStorageAdapter, UserAppPreferences } from "./storage/client-state/_types";
28
- export declare class ClientStateManager {
29
- /** In-memory cache warmed by hydrate(), kept in sync on every write. */
30
- private _cache;
31
- private _adapter;
32
- /**
33
- * @param adapter Defaults to InMemoryClientStateAdapter (non-persistent).
34
- * Pass IDBClientStateAdapter or SQLiteClientStateAdapter for persistence.
35
- */
27
+ import { MajikKeyClientStateManager } from "@majikah/majik-key-client";
28
+ import { ClientStateStorageAdapter, UserAppPreferences } from "./storage/client-state/_types";
29
+ export declare class ClientStateManager extends MajikKeyClientStateManager {
36
30
  constructor(adapter?: ClientStateStorageAdapter);
37
- get adapter(): ClientStateStorageAdapter;
38
- /**
39
- * Swap the storage adapter at runtime.
40
- *
41
- * Does NOT migrate data. To migrate:
42
- * ```ts
43
- * const entries = stateManager.listCachedEntries();
44
- * stateManager.setAdapter(newAdapter);
45
- * await stateManager.hydrate();
46
- * await stateManager.bulkSet(entries);
47
- * ```
48
- */
49
- setAdapter(adapter: ClientStateStorageAdapter): void;
50
- /**
51
- * Load all entries from the adapter into the in-memory cache.
52
- * Call once after construction (MajikSignatureClient.hydrate() does this).
53
- */
54
- hydrate(): Promise<void>;
55
- /**
56
- * Retrieve a raw JSON string for any key. Returns `null` if not found.
57
- * Prefer the typed accessors (getAccountOrder, getInvoiceDefaults) over this.
58
- */
59
- get(id: string): Promise<string | null>;
60
- /**
61
- * Persist a raw JSON string for any key.
62
- * Prefer the typed accessors over this.
63
- */
64
- set(id: string, value: string): Promise<void>;
65
- /**
66
- * Remove a single key.
67
- */
68
- remove(id: string): Promise<boolean>;
69
- /**
70
- * Remove all stored state.
71
- */
72
- clear(): Promise<void>;
73
- /**
74
- * Whether a key exists in the cache.
75
- * Accurate after hydrate(); use exists() for an authoritative adapter check.
76
- */
77
- hasCached(id: string): boolean;
78
- /**
79
- * Authoritative existence check against the adapter.
80
- */
81
- exists(id: string): Promise<boolean>;
82
- /**
83
- * Snapshot of all cached entries — useful for adapter migration.
84
- */
85
- listCachedEntries(): ClientStateEntry[];
86
- /**
87
- * Persist multiple entries in one adapter call.
88
- */
89
- bulkSet(entries: ClientStateEntry[]): Promise<void>;
90
- /**
91
- * Retrieve the ordered list of own account IDs.
92
- * Returns `null` if no order has been persisted yet.
93
- */
94
- getAccountOrder(): Promise<AccountOrderValue | null>;
95
- /**
96
- * Persist the ordered list of own account IDs.
97
- */
98
- setAccountOrder(order: AccountOrderValue): Promise<void>;
99
- /**
100
- * Remove the persisted account order (resets to insertion order on next
101
- * hydrate).
102
- */
103
- removeAccountOrder(): Promise<void>;
104
- count(): Promise<number>;
105
31
  /**
106
32
  * Retrieve user app preferences.
107
33
  * Returns `null` if none have been saved yet.
@@ -24,159 +24,15 @@
24
24
  * hydrate() pass. Callers should never need to hydrate() again unless the
25
25
  * adapter is swapped.
26
26
  */
27
+ import { MajikKeyClientStateManager } from "@majikah/majik-key-client";
27
28
  import { CLIENT_STATE_KEYS, } from "./storage/client-state/_types";
28
29
  import { InMemoryClientStateAdapter } from "./storage/client-state/adapter-memory";
29
30
  // ---------------------------------------------------------------------------
30
31
  // ClientStateManager
31
32
  // ---------------------------------------------------------------------------
32
- export class ClientStateManager {
33
- /** In-memory cache — warmed by hydrate(), kept in sync on every write. */
34
- _cache = new Map();
35
- _adapter;
36
- /**
37
- * @param adapter Defaults to InMemoryClientStateAdapter (non-persistent).
38
- * Pass IDBClientStateAdapter or SQLiteClientStateAdapter for persistence.
39
- */
33
+ export class ClientStateManager extends MajikKeyClientStateManager {
40
34
  constructor(adapter = new InMemoryClientStateAdapter()) {
41
- this._adapter = adapter;
42
- }
43
- // ── Adapter management ────────────────────────────────────────────────────
44
- get adapter() {
45
- return this._adapter;
46
- }
47
- /**
48
- * Swap the storage adapter at runtime.
49
- *
50
- * Does NOT migrate data. To migrate:
51
- * ```ts
52
- * const entries = stateManager.listCachedEntries();
53
- * stateManager.setAdapter(newAdapter);
54
- * await stateManager.hydrate();
55
- * await stateManager.bulkSet(entries);
56
- * ```
57
- */
58
- setAdapter(adapter) {
59
- this._adapter = adapter;
60
- }
61
- // ── Hydration ─────────────────────────────────────────────────────────────
62
- /**
63
- * Load all entries from the adapter into the in-memory cache.
64
- * Call once after construction (MajikSignatureClient.hydrate() does this).
65
- */
66
- async hydrate() {
67
- const entries = await this._adapter.list();
68
- this._cache.clear();
69
- for (const entry of entries) {
70
- this._cache.set(entry.id, entry.value);
71
- }
72
- }
73
- // ── Generic typed get / set / remove ─────────────────────────────────────
74
- /**
75
- * Retrieve a raw JSON string for any key. Returns `null` if not found.
76
- * Prefer the typed accessors (getAccountOrder, getInvoiceDefaults) over this.
77
- */
78
- async get(id) {
79
- const cached = this._cache.get(id);
80
- if (cached !== undefined)
81
- return cached;
82
- // Cache miss — should not happen after hydrate() but defensive
83
- const entry = await this._adapter.getById(id);
84
- if (!entry)
85
- return null;
86
- this._cache.set(id, entry.value);
87
- return entry.value;
88
- }
89
- /**
90
- * Persist a raw JSON string for any key.
91
- * Prefer the typed accessors over this.
92
- */
93
- async set(id, value) {
94
- const entry = { id, value };
95
- await this._adapter.save(entry);
96
- this._cache.set(id, value);
97
- }
98
- /**
99
- * Remove a single key.
100
- */
101
- async remove(id) {
102
- const removed = await this._adapter.remove(id);
103
- this._cache.delete(id);
104
- return removed;
105
- }
106
- /**
107
- * Remove all stored state.
108
- */
109
- async clear() {
110
- await this._adapter.clear();
111
- this._cache.clear();
112
- }
113
- /**
114
- * Whether a key exists in the cache.
115
- * Accurate after hydrate(); use exists() for an authoritative adapter check.
116
- */
117
- hasCached(id) {
118
- return this._cache.has(id);
119
- }
120
- /**
121
- * Authoritative existence check against the adapter.
122
- */
123
- async exists(id) {
124
- if (this._cache.has(id))
125
- return true;
126
- return this._adapter.exists(id);
127
- }
128
- /**
129
- * Snapshot of all cached entries — useful for adapter migration.
130
- */
131
- listCachedEntries() {
132
- return Array.from(this._cache.entries()).map(([id, value]) => ({
133
- id,
134
- value,
135
- }));
136
- }
137
- /**
138
- * Persist multiple entries in one adapter call.
139
- */
140
- async bulkSet(entries) {
141
- if (entries.length === 0)
142
- return;
143
- await this._adapter.bulkSave(entries);
144
- for (const e of entries)
145
- this._cache.set(e.id, e.value);
146
- }
147
- // ── Typed: account order ──────────────────────────────────────────────────
148
- /**
149
- * Retrieve the ordered list of own account IDs.
150
- * Returns `null` if no order has been persisted yet.
151
- */
152
- async getAccountOrder() {
153
- const raw = await this.get(CLIENT_STATE_KEYS.ACCOUNT_ORDER);
154
- if (raw === null)
155
- return null;
156
- try {
157
- return JSON.parse(raw);
158
- }
159
- catch {
160
- console.warn("ClientStateManager: malformed account order — discarding.");
161
- return null;
162
- }
163
- }
164
- /**
165
- * Persist the ordered list of own account IDs.
166
- */
167
- async setAccountOrder(order) {
168
- await this.set(CLIENT_STATE_KEYS.ACCOUNT_ORDER, JSON.stringify(order));
169
- }
170
- /**
171
- * Remove the persisted account order (resets to insertion order on next
172
- * hydrate).
173
- */
174
- async removeAccountOrder() {
175
- await this.remove(CLIENT_STATE_KEYS.ACCOUNT_ORDER);
176
- }
177
- // ── Async count ───────────────────────────────────────────────────────────
178
- async count() {
179
- return this._adapter.count();
35
+ super(adapter);
180
36
  }
181
37
  /**
182
38
  * Retrieve user app preferences.
@@ -1,6 +1,7 @@
1
1
  import { MAJIK_API_RESPONSE } from "../types";
2
2
  import { MajikContact, MajikContactData } from "@majikah/majik-contact";
3
3
  import { MajikContactDirectoryData } from "./types";
4
+ import { MajikKeyAddress } from "@majikah/majik-key";
4
5
  export declare class MajikContactDirectory {
5
6
  private contacts;
6
7
  private fingerprintMap;
@@ -15,7 +16,7 @@ export declare class MajikContactDirectory {
15
16
  * Get contact by public key (base64)
16
17
  * Uses MajikContact.getPublicKeyBase64() for canonical comparison
17
18
  */
18
- getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
19
+ getContactByAddress(address: MajikKeyAddress): Promise<MajikContact | undefined>;
19
20
  hasFingerprint(fingerprint: string): boolean;
20
21
  listContacts(sortedByLabel?: boolean, majikahOnly?: boolean): MajikContact[];
21
22
  blockContact(id: string): MajikContact;
@@ -24,7 +25,7 @@ export declare class MajikContactDirectory {
24
25
  /**
25
26
  * Checks if a contact exists by their public key (base64)
26
27
  */
27
- hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
28
+ hasContactByAddress(address: MajikKeyAddress): Promise<boolean>;
28
29
  clear(): this;
29
30
  setMajikahStatus(id: string, status: boolean): MajikContact;
30
31
  isMajikahIdentityChecked(id: string): boolean;
@@ -78,13 +78,13 @@ export class MajikContactDirectory {
78
78
  * Get contact by public key (base64)
79
79
  * Uses MajikContact.getPublicKeyBase64() for canonical comparison
80
80
  */
81
- async getContactByPublicKeyBase64(publicKeyBase64) {
82
- if (!publicKeyBase64 || typeof publicKeyBase64 !== "string") {
83
- throw new MajikContactDirectoryError("Public key must be a non-empty base64 string");
81
+ async getContactByAddress(address) {
82
+ if (!address || typeof address !== "string") {
83
+ throw new MajikContactDirectoryError("Public key must be a non-empty base64 MajikKeyAddress");
84
84
  }
85
85
  for (const contact of this.contacts.values()) {
86
86
  const contactKey = await contact.getPublicKeyBase64();
87
- if (contactKey === publicKeyBase64) {
87
+ if (contactKey === address) {
88
88
  return contact;
89
89
  }
90
90
  }
@@ -121,11 +121,11 @@ export class MajikContactDirectory {
121
121
  /**
122
122
  * Checks if a contact exists by their public key (base64)
123
123
  */
124
- async hasContactByPublicKeyBase64(publicKeyBase64) {
125
- if (!publicKeyBase64 || typeof publicKeyBase64 !== "string") {
126
- throw new MajikContactDirectoryError("Public key must be a non-empty base64 string");
124
+ async hasContactByAddress(address) {
125
+ if (!address || typeof address !== "string") {
126
+ throw new MajikContactDirectoryError("Public key must be a non-empty base64 MajikKeyAddress");
127
127
  }
128
- const contact = await this.getContactByPublicKeyBase64(publicKeyBase64);
128
+ const contact = await this.getContactByAddress(address);
129
129
  return contact !== undefined;
130
130
  }
131
131
  clear() {
@@ -5,6 +5,7 @@ import { MAJIK_API_RESPONSE } from "../types";
5
5
  import { MajikContactManagerJSON } from "./types";
6
6
  import { MajikContactStorageAdapter } from "../storage/contact-directory/contacts/_types";
7
7
  import { MajikContactGroupStorageAdapter } from "../storage/contact-directory/groups/_types";
8
+ import { MajikKeyAddress } from "@majikah/majik-key";
8
9
  export interface MajikContactManagerAdapters {
9
10
  contacts?: MajikContactStorageAdapter;
10
11
  groups?: MajikContactGroupStorageAdapter;
@@ -82,12 +83,12 @@ export declare class MajikContactManager {
82
83
  clear(): Promise<this>;
83
84
  getContact(id: string): MajikContact | undefined;
84
85
  getContactByFingerprint(fingerprint: string): MajikContact | undefined;
85
- getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
86
+ getContactByAddress(address: MajikKeyAddress): Promise<MajikContact | undefined>;
86
87
  getContactsByIds(ids: string[], strict?: boolean): MajikContact[];
87
88
  getContactsByPublicKeys(publicKeys: string[], strict?: boolean): Promise<MajikContact[]>;
88
89
  hasContact(id: string): boolean;
89
90
  hasFingerprint(fingerprint: string): boolean;
90
- hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
91
+ hasContactByAddress(address: MajikKeyAddress): Promise<boolean>;
91
92
  listContacts(sortedByLabel?: boolean, majikahOnly?: boolean): MajikContact[];
92
93
  isMajikahRegistered(id: string): boolean;
93
94
  isMajikahIdentityChecked(id: string): boolean;
@@ -190,8 +190,8 @@ export class MajikContactManager {
190
190
  getContactByFingerprint(fingerprint) {
191
191
  return this.directory.getContactByFingerprint(fingerprint);
192
192
  }
193
- async getContactByPublicKeyBase64(publicKeyBase64) {
194
- return await this.directory.getContactByPublicKeyBase64(publicKeyBase64);
193
+ async getContactByAddress(address) {
194
+ return await this.directory.getContactByAddress(address);
195
195
  }
196
196
  getContactsByIds(ids, strict = false) {
197
197
  if (!ids?.length)
@@ -218,7 +218,7 @@ export class MajikContactManager {
218
218
  return [];
219
219
  const uniqueKeys = [...new Set(publicKeys)];
220
220
  const contacts = await Promise.all(uniqueKeys.map(async (key) => {
221
- const contact = await this.directory.getContactByPublicKeyBase64(key);
221
+ const contact = await this.directory.getContactByAddress(key);
222
222
  if (!contact && strict) {
223
223
  throw new MajikContactManagerError(`Contact not found for publicKey: ${key}`);
224
224
  }
@@ -232,8 +232,8 @@ export class MajikContactManager {
232
232
  hasFingerprint(fingerprint) {
233
233
  return this.directory.hasFingerprint(fingerprint);
234
234
  }
235
- async hasContactByPublicKeyBase64(publicKeyBase64) {
236
- return this.directory.hasContactByPublicKeyBase64(publicKeyBase64);
235
+ async hasContactByAddress(address) {
236
+ return this.directory.hasContactByAddress(address);
237
237
  }
238
238
  listContacts(sortedByLabel = false, majikahOnly = false) {
239
239
  return this.directory.listContacts(sortedByLabel, majikahOnly);
@@ -7,15 +7,8 @@
7
7
  * ClientStateManager owns all serialisation / deserialisation logic; the
8
8
  * adapter only moves bytes.
9
9
  */
10
+ import { ClientStateEntry } from "@majikah/majik-key-client";
10
11
  import { MajikStorageAdapter } from "../storage-adapter";
11
- export interface ClientStateEntry {
12
- /** Stable string key that identifies this piece of state. */
13
- id: string;
14
- /** JSON-serialised value. Always a string on the wire / in storage. */
15
- value: string;
16
- /** ISO 8601 datetime — set by the adapter on every write where supported. */
17
- updatedAt?: string;
18
- }
19
12
  export declare const CLIENT_STATE_KEYS: {
20
13
  readonly ACCOUNT_ORDER: "user_account_order";
21
14
  readonly USER_APP_PREFERENCES: "user_app_preferences";
@@ -7,10 +7,11 @@
7
7
  * ClientStateManager owns all serialisation / deserialisation logic; the
8
8
  * adapter only moves bytes.
9
9
  */
10
+ import { BASE_CLIENT_STATE_KEYS } from "@majikah/majik-key-client";
10
11
  // ---------------------------------------------------------------------------
11
12
  // Well-known state keys
12
13
  // ---------------------------------------------------------------------------
13
14
  export const CLIENT_STATE_KEYS = {
14
- ACCOUNT_ORDER: "user_account_order",
15
+ ...BASE_CLIENT_STATE_KEYS,
15
16
  USER_APP_PREFERENCES: "user_app_preferences",
16
17
  };
@@ -12,6 +12,6 @@
12
12
  * Each entry is stored with `id` as the keyPath — identical to the invoice
13
13
  * and contact adapters.
14
14
  */
15
- import type { ClientStateEntry } from "./_types";
15
+ import type { ClientStateEntry } from "@majikah/majik-key-client";
16
16
  import { IDBGenericAdapter } from "../idb-adapter";
17
17
  export declare const IDB_ADAPTER_CLIENT_STATE: IDBGenericAdapter<ClientStateEntry>;
@@ -5,7 +5,8 @@
5
5
  * Default adapter — zero-config, non-persistent. State is lost on page reload
6
6
  * or process restart. Useful for tests, SSR, and headless environments.
7
7
  */
8
- import type { ClientStateEntry, ClientStateStorageAdapter } from "./_types";
8
+ import type { ClientStateEntry } from "@majikah/majik-key-client";
9
+ import type { ClientStateStorageAdapter } from "./_types";
9
10
  export declare class InMemoryClientStateAdapter implements ClientStateStorageAdapter {
10
11
  private _store;
11
12
  save(entry: ClientStateEntry): Promise<void>;
@@ -17,8 +17,9 @@
17
17
  * needed) and mapped to the `id` field of ClientStateEntry in TypeScript so
18
18
  * the adapter contract is identical to the IDB and memory variants.
19
19
  */
20
- import type { ClientStateEntry, ClientStateStorageAdapter } from "./_types";
20
+ import type { ClientStateStorageAdapter } from "./_types";
21
21
  import type { SQLiteDatabase } from "../sql-db-manager";
22
+ import type { ClientStateEntry } from "@majikah/majik-key-client";
22
23
  /** DDL — pass to your migration runner or call createSchema() directly. */
23
24
  export declare const MAJIK_CLIENT_STATE_SCHEMA: string;
24
25
  export declare class SQLiteClientStateAdapter implements ClientStateStorageAdapter {
@@ -19,12 +19,12 @@
19
19
  */
20
20
  const TABLE = "majik_client_state";
21
21
  /** DDL — pass to your migration runner or call createSchema() directly. */
22
- export const MAJIK_CLIENT_STATE_SCHEMA = `
23
- CREATE TABLE IF NOT EXISTS ${TABLE} (
24
- key TEXT PRIMARY KEY,
25
- value TEXT NOT NULL,
26
- updated_at TEXT DEFAULT (datetime('now'))
27
- );
22
+ export const MAJIK_CLIENT_STATE_SCHEMA = `
23
+ CREATE TABLE IF NOT EXISTS ${TABLE} (
24
+ key TEXT PRIMARY KEY,
25
+ value TEXT NOT NULL,
26
+ updated_at TEXT DEFAULT (datetime('now'))
27
+ );
28
28
  `.trim();
29
29
  // ---------------------------------------------------------------------------
30
30
  // Adapter
@@ -44,7 +44,7 @@ export class SQLiteClientStateAdapter {
44
44
  // }
45
45
  // ── Write ──────────────────────────────────────────────────────────────────
46
46
  async save(entry) {
47
- await this.db.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
47
+ await this.db.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
48
48
  VALUES (?, ?, datetime('now'))`, [entry.id, entry.value]);
49
49
  }
50
50
  async bulkSave(entries) {
@@ -52,7 +52,7 @@ export class SQLiteClientStateAdapter {
52
52
  return;
53
53
  await this.db.transaction(async (tx) => {
54
54
  for (const entry of entries) {
55
- await tx.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
55
+ await tx.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
56
56
  VALUES (?, ?, datetime('now'))`, [entry.id, entry.value]);
57
57
  }
58
58
  });
@@ -5,8 +5,8 @@ export class SQLiteContactAdapter {
5
5
  this.db = db;
6
6
  }
7
7
  async save(contact) {
8
- await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
9
- (id, json, fingerprint, label, created_at, updated_at)
8
+ await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
9
+ (id, json, fingerprint, label, created_at, updated_at)
10
10
  VALUES (?, ?, ?, ?, ?, ?)`, [
11
11
  contact.id,
12
12
  JSON.stringify(contact),
@@ -47,8 +47,8 @@ export class SQLiteContactAdapter {
47
47
  return;
48
48
  await this.db.transaction(async (tx) => {
49
49
  for (const c of contacts) {
50
- await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
51
- (id, json, fingerprint, label, created_at, updated_at)
50
+ await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
51
+ (id, json, fingerprint, label, created_at, updated_at)
52
52
  VALUES (?, ?, ?, ?, ?, ?)`, [
53
53
  c.id,
54
54
  JSON.stringify(c),
@@ -5,8 +5,8 @@ export class SQLiteContactGroupAdapter {
5
5
  this.db = db;
6
6
  }
7
7
  async save(group) {
8
- await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
9
- (id, json, name, created_at, updated_at, is_system)
8
+ await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
9
+ (id, json, name, created_at, updated_at, is_system)
10
10
  VALUES (?, ?, ?, ?, ?, ?)`, [
11
11
  group.id,
12
12
  JSON.stringify(group),
@@ -47,8 +47,8 @@ export class SQLiteContactGroupAdapter {
47
47
  return;
48
48
  await this.db.transaction(async (tx) => {
49
49
  for (const g of groups) {
50
- await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
51
- (id, json, name, created_at, updated_at, is_system)
50
+ await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
51
+ (id, json, name, created_at, updated_at, is_system)
52
52
  VALUES (?, ?, ?, ?, ?, ?)`, [
53
53
  g.id,
54
54
  JSON.stringify(g),
@@ -5,8 +5,8 @@ export class SQLiteKeystoreAdapter {
5
5
  this.db = db;
6
6
  }
7
7
  async save(key) {
8
- await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
9
- (id, json, timestamp, public_key)
8
+ await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
9
+ (id, json, timestamp, public_key)
10
10
  VALUES (?, ?, ?, ?)`, [
11
11
  key.id,
12
12
  JSON.stringify(key),
@@ -45,8 +45,8 @@ export class SQLiteKeystoreAdapter {
45
45
  return;
46
46
  await this.db.transaction(async (tx) => {
47
47
  for (const g of keys) {
48
- await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
49
- (id, json, timestamp, public_key)
48
+ await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
49
+ (id, json, timestamp, public_key)
50
50
  VALUES (?, ?, ?, ?)`, [
51
51
  g.id,
52
52
  JSON.stringify(g),