@majikah/majik-universal-id-client 0.0.16 → 0.1.0

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 (30) hide show
  1. package/dist/core/client-state-manager.d.ts +13 -69
  2. package/dist/core/client-state-manager.js +40 -202
  3. package/dist/core/contacts/majik-contact-directory.d.ts +3 -2
  4. package/dist/core/contacts/majik-contact-directory.js +8 -11
  5. package/dist/core/contacts/majik-contact-manager.d.ts +4 -7
  6. package/dist/core/contacts/majik-contact-manager.js +6 -73
  7. package/dist/core/storage/client-state/_types.d.ts +39 -3
  8. package/dist/core/storage/client-state/_types.js +3 -2
  9. package/dist/core/storage/client-state/adapter-sql.js +8 -8
  10. package/dist/core/storage/contact-directory/contacts/adapter-sql.js +4 -4
  11. package/dist/core/storage/contact-directory/groups/adapter-sql.js +4 -4
  12. package/dist/core/storage/keystore/adapter-sql.js +4 -4
  13. package/dist/core/storage/sql-schema.js +43 -43
  14. package/dist/core/types.d.ts +0 -44
  15. package/dist/core/types.js +6 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.js +2 -0
  18. package/dist/majik-universal-id-client.d.ts +79 -158
  19. package/dist/majik-universal-id-client.js +296 -457
  20. package/package.json +10 -10
  21. package/dist/core/contacts/majik-contact.d.ts +0 -89
  22. package/dist/core/contacts/majik-contact.js +0 -212
  23. package/dist/core/crypto/keystore.d.ts +0 -228
  24. package/dist/core/crypto/keystore.js +0 -575
  25. package/dist/core/utils/APITranscoder.d.ts +0 -114
  26. package/dist/core/utils/APITranscoder.js +0 -305
  27. package/dist/core/utils/idb-majik-system.d.ts +0 -15
  28. package/dist/core/utils/idb-majik-system.js +0 -44
  29. package/dist/core/utils/majik-file-utils.d.ts +0 -16
  30. package/dist/core/utils/majik-file-utils.js +0 -153
@@ -24,82 +24,26 @@
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 } 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 (MajikBuwizClient.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
31
  /**
87
- * Persist multiple entries in one adapter call.
32
+ * Retrieve user app preferences.
33
+ * Returns `null` if none have been saved yet.
88
34
  */
89
- bulkSet(entries: ClientStateEntry[]): Promise<void>;
35
+ getUserAppPreferences(): Promise<UserAppPreferences>;
90
36
  /**
91
- * Retrieve the ordered list of own account IDs.
92
- * Returns `null` if no order has been persisted yet.
37
+ * Persist user app preferences.
93
38
  */
94
- getAccountOrder(): Promise<AccountOrderValue | null>;
39
+ setUserAppPreferences(preferences: UserAppPreferences): Promise<void>;
95
40
  /**
96
- * Persist the ordered list of own account IDs.
41
+ * Persist user app preferences.
97
42
  */
98
- setAccountOrder(order: AccountOrderValue): Promise<void>;
43
+ resetUserAppPreferences(): Promise<void>;
99
44
  /**
100
- * Remove the persisted account order (resets to insertion order on next
101
- * hydrate).
45
+ * Remove user app preferences.
102
46
  */
103
- removeAccountOrder(): Promise<void>;
104
- count(): Promise<number>;
47
+ removeUserAppPreferences(): Promise<void>;
105
48
  }
49
+ export declare const DEFAULT_USER_APP_PREFERENCES: UserAppPreferences;
@@ -24,227 +24,65 @@
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;
35
+ super(adapter);
46
36
  }
47
37
  /**
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
- * ```
38
+ * Retrieve user app preferences.
39
+ * Returns `null` if none have been saved yet.
57
40
  */
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 (MajikBuwizClient.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);
41
+ async getUserAppPreferences() {
42
+ const raw = await this.get(CLIENT_STATE_KEYS.USER_APP_PREFERENCES);
43
+ if (raw === null) {
44
+ await this.resetUserAppPreferences();
45
+ return DEFAULT_USER_APP_PREFERENCES;
71
46
  }
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
47
  try {
157
- return JSON.parse(raw);
48
+ return {
49
+ ...DEFAULT_USER_APP_PREFERENCES,
50
+ ...JSON.parse(raw),
51
+ };
158
52
  }
159
- catch {
160
- console.warn("ClientStateManager: malformed account order discarding.");
161
- return null;
53
+ catch (e) {
54
+ console.warn("ClientStateManager: Problem retrieving user app preferences: ", e);
55
+ return DEFAULT_USER_APP_PREFERENCES;
162
56
  }
163
57
  }
164
58
  /**
165
- * Persist the ordered list of own account IDs.
59
+ * Persist user app preferences.
166
60
  */
167
- async setAccountOrder(order) {
168
- await this.set(CLIENT_STATE_KEYS.ACCOUNT_ORDER, JSON.stringify(order));
61
+ async setUserAppPreferences(preferences) {
62
+ await this.set(CLIENT_STATE_KEYS.USER_APP_PREFERENCES, JSON.stringify(preferences));
169
63
  }
170
64
  /**
171
- * Remove the persisted account order (resets to insertion order on next
172
- * hydrate).
65
+ * Persist user app preferences.
173
66
  */
174
- async removeAccountOrder() {
175
- await this.remove(CLIENT_STATE_KEYS.ACCOUNT_ORDER);
67
+ async resetUserAppPreferences() {
68
+ await this.set(CLIENT_STATE_KEYS.USER_APP_PREFERENCES, JSON.stringify(DEFAULT_USER_APP_PREFERENCES));
176
69
  }
177
- // ── Typed: invoice defaults ───────────────────────────────────────────────
178
- // /**
179
- // * Retrieve user-configured invoice defaults.
180
- // * Returns `null` if none have been saved yet.
181
- // */
182
- // async getInvoiceDefaults(): Promise<InvoiceDefaults | null> {
183
- // const raw = await this.get(CLIENT_STATE_KEYS.INVOICE_DEFAULTS);
184
- // if (raw === null) return null;
185
- // try {
186
- // return JSON.parse(raw) as InvoiceDefaults;
187
- // } catch {
188
- // console.warn(
189
- // "ClientStateManager: malformed invoice defaults — discarding.",
190
- // );
191
- // return null;
192
- // }
193
- // }
194
- // /**
195
- // * Persist user-configured invoice defaults.
196
- // */
197
- // async setInvoiceDefaults(defaults: InvoiceDefaults): Promise<void> {
198
- // await this.set(
199
- // CLIENT_STATE_KEYS.INVOICE_DEFAULTS,
200
- // JSON.stringify(defaults),
201
- // );
202
- // }
203
- // /**
204
- // * Remove the persisted invoice defaults.
205
- // */
206
- // async removeInvoiceDefaults(): Promise<void> {
207
- // await this.remove(CLIENT_STATE_KEYS.INVOICE_DEFAULTS);
208
- // }
209
- // async currentInvoiceNumber(): Promise<number> {
210
- // const current = await this.getInvoiceDefaults();
211
- // const counter = current?.invoiceNumberCounter ?? 0;
212
- // return counter;
213
- // }
214
- // async incrementInvoiceNumber(): Promise<number> {
215
- // // 1. Get current defaults
216
- // const current = await this.getInvoiceDefaults();
217
- // // 2. Initialize safely if missing
218
- // const counter = current?.invoiceNumberCounter ?? 0;
219
- // const updated: InvoiceDefaults = {
220
- // ...(current ?? {
221
- // currency: "PHP" as any, // fallback — adjust if you have a real default
222
- // }),
223
- // invoiceNumberCounter: counter + 1,
224
- // };
225
- // // 3. Persist
226
- // await this.setInvoiceDefaults(updated);
227
- // // 4. Return the new value (useful for generating invoice number)
228
- // return updated.invoiceNumberCounter!;
229
- // }
230
- // async decrementInvoiceNumber(): Promise<number> {
231
- // // 1. Get current defaults
232
- // const current = await this.getInvoiceDefaults();
233
- // // 2. Initialize safely if missing
234
- // const counter = current?.invoiceNumberCounter ?? 0;
235
- // const updated: InvoiceDefaults = {
236
- // ...(current ?? {
237
- // currency: "PHP" as any, // fallback — adjust if you have a real default
238
- // }),
239
- // invoiceNumberCounter: counter - 1,
240
- // };
241
- // // 3. Persist
242
- // await this.setInvoiceDefaults(updated);
243
- // // 4. Return the new value (useful for generating invoice number)
244
- // return updated.invoiceNumberCounter!;
245
- // }
246
- // ── Async count ───────────────────────────────────────────────────────────
247
- async count() {
248
- return this._adapter.count();
70
+ /**
71
+ * Remove user app preferences.
72
+ */
73
+ async removeUserAppPreferences() {
74
+ await this.remove(CLIENT_STATE_KEYS.USER_APP_PREFERENCES);
249
75
  }
250
76
  }
77
+ export const DEFAULT_USER_APP_PREFERENCES = {
78
+ general: {},
79
+ privacy: {
80
+ shareAnalytics: true,
81
+ },
82
+ security: {
83
+ key: {
84
+ autoLockOnMinimize: false,
85
+ onetimeUnlock: true,
86
+ },
87
+ },
88
+ };
@@ -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;
@@ -23,9 +23,6 @@ export class MajikContactDirectory {
23
23
  !contact?.mlKey) {
24
24
  throw new MajikContactDirectoryError("Invalid contact");
25
25
  }
26
- if (!(contact instanceof MajikContact)) {
27
- throw new MajikContactDirectoryError("Invalid contact instance");
28
- }
29
26
  if (this.contacts.has(contact.id)) {
30
27
  throw new MajikContactDirectoryError(`Contact with id "${contact.id}" already exists`);
31
28
  }
@@ -81,13 +78,13 @@ export class MajikContactDirectory {
81
78
  * Get contact by public key (base64)
82
79
  * Uses MajikContact.getPublicKeyBase64() for canonical comparison
83
80
  */
84
- async getContactByPublicKeyBase64(publicKeyBase64) {
85
- if (!publicKeyBase64 || typeof publicKeyBase64 !== "string") {
86
- 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");
87
84
  }
88
85
  for (const contact of this.contacts.values()) {
89
86
  const contactKey = await contact.getPublicKeyBase64();
90
- if (contactKey === publicKeyBase64) {
87
+ if (contactKey === address) {
91
88
  return contact;
92
89
  }
93
90
  }
@@ -124,11 +121,11 @@ export class MajikContactDirectory {
124
121
  /**
125
122
  * Checks if a contact exists by their public key (base64)
126
123
  */
127
- async hasContactByPublicKeyBase64(publicKeyBase64) {
128
- if (!publicKeyBase64 || typeof publicKeyBase64 !== "string") {
129
- 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");
130
127
  }
131
- const contact = await this.getContactByPublicKeyBase64(publicKeyBase64);
128
+ const contact = await this.getContactByAddress(address);
132
129
  return contact !== undefined;
133
130
  }
134
131
  clear() {
@@ -2,11 +2,10 @@ import { MajikContact, MajikContactData, MajikContactGroup, MajikContactGroupMet
2
2
  import { MajikContactDirectory } from "./majik-contact-directory";
3
3
  import { MajikContactGroupManager } from "./majik-contact-groups";
4
4
  import { MAJIK_API_RESPONSE } from "../types";
5
- import { ContactManagerQueryMode, MajikContactManagerJSON } from "./types";
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 { MajikRecipient } from "@majikah/majik-envelope";
9
- import { ExpectedSigner } from "@majikah/majik-signature";
8
+ import { MajikKeyAddress } from "@majikah/majik-key";
10
9
  export interface MajikContactManagerAdapters {
11
10
  contacts?: MajikContactStorageAdapter;
12
11
  groups?: MajikContactGroupStorageAdapter;
@@ -84,14 +83,12 @@ export declare class MajikContactManager {
84
83
  clear(): Promise<this>;
85
84
  getContact(id: string): MajikContact | undefined;
86
85
  getContactByFingerprint(fingerprint: string): MajikContact | undefined;
87
- getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
86
+ getContactByAddress(address: MajikKeyAddress): Promise<MajikContact | undefined>;
88
87
  getContactsByIds(ids: string[], strict?: boolean): MajikContact[];
89
88
  getContactsByPublicKeys(publicKeys: string[], strict?: boolean): Promise<MajikContact[]>;
90
- getMajikRecipients(mode: ContactManagerQueryMode | undefined, input: string[], strict?: boolean): Promise<MajikRecipient[]>;
91
- getExpectedSigners(mode: ContactManagerQueryMode | undefined, input: string[], strict?: boolean): Promise<ExpectedSigner[]>;
92
89
  hasContact(id: string): boolean;
93
90
  hasFingerprint(fingerprint: string): boolean;
94
- hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
91
+ hasContactByAddress(address: MajikKeyAddress): Promise<boolean>;
95
92
  listContacts(sortedByLabel?: boolean, majikahOnly?: boolean): MajikContact[];
96
93
  isMajikahRegistered(id: string): boolean;
97
94
  isMajikahIdentityChecked(id: string): boolean;
@@ -2,12 +2,11 @@ import { MajikContact, } from "@majikah/majik-contact";
2
2
  import { MajikContactDirectory } from "./majik-contact-directory";
3
3
  import { MajikContactGroupManager } from "./majik-contact-groups";
4
4
  import { MajikContactManagerError } from "./errors";
5
- import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, base64ToUint8Array, } from "../utils/utilities";
5
+ import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, } from "../utils/utilities";
6
6
  import { KEY_ALGO } from "../crypto/constants";
7
7
  import { gunzipSync, gzipSync } from "fflate";
8
8
  import { InMemoryContactAdapter } from "../storage/contact-directory/contacts/adapter-memory";
9
9
  import { InMemoryContactGroupAdapter } from "../storage/contact-directory/groups/adapter-memory";
10
- import { MajikEnvelope } from "@majikah/majik-envelope";
11
10
  export class MajikContactManager {
12
11
  directory;
13
12
  groupManager;
@@ -191,8 +190,8 @@ export class MajikContactManager {
191
190
  getContactByFingerprint(fingerprint) {
192
191
  return this.directory.getContactByFingerprint(fingerprint);
193
192
  }
194
- async getContactByPublicKeyBase64(publicKeyBase64) {
195
- return await this.directory.getContactByPublicKeyBase64(publicKeyBase64);
193
+ async getContactByAddress(address) {
194
+ return await this.directory.getContactByAddress(address);
196
195
  }
197
196
  getContactsByIds(ids, strict = false) {
198
197
  if (!ids?.length)
@@ -219,7 +218,7 @@ export class MajikContactManager {
219
218
  return [];
220
219
  const uniqueKeys = [...new Set(publicKeys)];
221
220
  const contacts = await Promise.all(uniqueKeys.map(async (key) => {
222
- const contact = await this.directory.getContactByPublicKeyBase64(key);
221
+ const contact = await this.directory.getContactByAddress(key);
223
222
  if (!contact && strict) {
224
223
  throw new MajikContactManagerError(`Contact not found for publicKey: ${key}`);
225
224
  }
@@ -227,80 +226,14 @@ export class MajikContactManager {
227
226
  }));
228
227
  return contacts.filter((c) => Boolean(c));
229
228
  }
230
- async getMajikRecipients(mode = "id", input, strict) {
231
- if (!input?.length)
232
- throw new MajikContactManagerError("At least 1 id/key is required");
233
- const contacts = mode === "public_key"
234
- ? await this.getContactsByPublicKeys(input, strict)
235
- : this.getContactsByIds(input, strict);
236
- if (!contacts || contacts.length === 0)
237
- return [];
238
- const recipients = [];
239
- const seen = new Set();
240
- const invalidContacts = [];
241
- for (const contact of contacts) {
242
- if (!contact)
243
- continue;
244
- // dedupe by fingerprint
245
- if (seen.has(contact.fingerprint))
246
- continue;
247
- const mlPubKey = base64ToUint8Array(contact.mlKey);
248
- if (!mlPubKey) {
249
- invalidContacts.push(contact.fingerprint);
250
- continue;
251
- }
252
- const builtMajikRecipient = await MajikEnvelope.buildMajikRecipientFromContact(contact);
253
- recipients.push(builtMajikRecipient);
254
- seen.add(contact.fingerprint);
255
- }
256
- if (invalidContacts.length > 0) {
257
- throw new MajikContactManagerError(`Invalid ML-KEM public key for contact(s): ${invalidContacts.join(", ")}`);
258
- }
259
- return recipients;
260
- }
261
- async getExpectedSigners(mode = "id", input, strict) {
262
- if (!input?.length)
263
- throw new MajikContactManagerError("At least 1 id/key is required");
264
- const contacts = mode === "public_key"
265
- ? await this.getContactsByPublicKeys(input, strict)
266
- : this.getContactsByIds(input, strict);
267
- if (!contacts || contacts.length === 0)
268
- return [];
269
- const signers = [];
270
- const seen = new Set();
271
- const invalidContacts = [];
272
- for (const contact of contacts) {
273
- if (!contact)
274
- continue;
275
- // dedupe by fingerprint
276
- if (seen.has(contact.fingerprint))
277
- continue;
278
- const mlDsaPublicKey = contact.mlDsaPublicKeyBase64;
279
- if (!mlDsaPublicKey?.trim()) {
280
- invalidContacts.push(contact.fingerprint);
281
- continue;
282
- }
283
- const expectedSigner = {
284
- edPublicKey: contact.edPublicKeyBase64,
285
- mlDsaPublicKey: contact.mlDsaPublicKeyBase64,
286
- signerId: contact.fingerprint,
287
- };
288
- signers.push(expectedSigner);
289
- seen.add(contact.fingerprint);
290
- }
291
- if (invalidContacts.length > 0) {
292
- throw new MajikContactManagerError(`Invalid ML-KEM public key for contact(s): ${invalidContacts.join(", ")}`);
293
- }
294
- return signers;
295
- }
296
229
  hasContact(id) {
297
230
  return this.directory.hasContact(id);
298
231
  }
299
232
  hasFingerprint(fingerprint) {
300
233
  return this.directory.hasFingerprint(fingerprint);
301
234
  }
302
- async hasContactByPublicKeyBase64(publicKeyBase64) {
303
- return this.directory.hasContactByPublicKeyBase64(publicKeyBase64);
235
+ async hasContactByAddress(address) {
236
+ return this.directory.hasContactByAddress(address);
304
237
  }
305
238
  listContacts(sortedByLabel = false, majikahOnly = false) {
306
239
  return this.directory.listContacts(sortedByLabel, majikahOnly);
@@ -18,13 +18,49 @@ export interface ClientStateEntry {
18
18
  }
19
19
  export declare const CLIENT_STATE_KEYS: {
20
20
  readonly ACCOUNT_ORDER: "user_account_order";
21
+ readonly USER_APP_PREFERENCES: "user_app_preferences";
21
22
  };
22
23
  export type ClientStateKey = (typeof CLIENT_STATE_KEYS)[keyof typeof CLIENT_STATE_KEYS];
23
24
  /**
24
- * Ordered list of own account IDs. The head of the array is the active
25
- * account. Stored as a JSON array: `["id1", "id2", ...]`.
25
+ * User-configured app-wide preferences.
26
26
  */
27
- export type AccountOrderValue = string[];
27
+ export interface UserAppPreferences {
28
+ general: GeneralPreferences;
29
+ privacy: PrivacyPreferences;
30
+ security: SecurityPreferences;
31
+ }
32
+ export interface GeneralPreferences {
33
+ history?: HistoryPreferences;
34
+ }
35
+ export interface HistoryPreferences {
36
+ enabled?: boolean;
37
+ maxCount?: number;
38
+ }
39
+ export interface SigningPreferences {
40
+ autoSeal?: boolean;
41
+ defaultToTSA?: boolean;
42
+ defaultToDetached?: boolean;
43
+ autosave?: {
44
+ afterSign?: AutosavePreferences;
45
+ afterSeal?: AutosavePreferences;
46
+ afterNotary?: AutosavePreferences;
47
+ };
48
+ }
49
+ export interface AutosavePreferences {
50
+ enabled?: boolean;
51
+ defaultPath?: string;
52
+ }
53
+ export interface PrivacyPreferences {
54
+ shareAnalytics?: boolean;
55
+ }
56
+ export interface SecurityPreferences {
57
+ key?: KeyPreferences;
58
+ }
59
+ export interface KeyPreferences {
60
+ autoLockOnMinimize?: boolean;
61
+ autoLockInterval?: number;
62
+ onetimeUnlock?: boolean;
63
+ }
28
64
  /**
29
65
  * Pluggable persistence backend for client-level state.
30
66
  *
@@ -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
- // INVOICE_DEFAULTS: "invoice_defaults",
15
+ ...BASE_CLIENT_STATE_KEYS,
16
+ USER_APP_PREFERENCES: "user_app_preferences",
16
17
  };