@majikah/majik-message 0.3.6 → 0.3.7

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 (66) hide show
  1. package/README.md +3 -3
  2. package/dist/core/client-state-manager.d.ts +105 -0
  3. package/dist/core/client-state-manager.js +250 -0
  4. package/dist/core/contacts/majik-contact-directory.d.ts +0 -5
  5. package/dist/core/contacts/majik-contact-directory.js +0 -12
  6. package/dist/core/contacts/majik-contact-groups.d.ts +1 -0
  7. package/dist/core/contacts/majik-contact-groups.js +5 -0
  8. package/dist/core/contacts/majik-contact-manager.d.ts +92 -184
  9. package/dist/core/contacts/majik-contact-manager.js +368 -288
  10. package/dist/core/crypto/keystore-manager.d.ts +166 -0
  11. package/dist/core/crypto/keystore-manager.js +371 -0
  12. package/dist/core/storage/chats/_types.d.ts +8 -0
  13. package/dist/core/storage/chats/_types.js +1 -0
  14. package/dist/core/storage/chats/adapter-idb.d.ts +3 -0
  15. package/dist/core/storage/chats/adapter-idb.js +5 -0
  16. package/dist/core/storage/chats/adapter-memory.d.ts +23 -0
  17. package/dist/core/storage/chats/adapter-memory.js +44 -0
  18. package/dist/core/storage/chats/adapter-sql.d.ts +17 -0
  19. package/dist/core/storage/chats/adapter-sql.js +84 -0
  20. package/dist/core/storage/client-state/_types.d.ts +37 -0
  21. package/dist/core/storage/client-state/_types.js +16 -0
  22. package/dist/core/storage/client-state/adapter-idb.d.ts +17 -0
  23. package/dist/core/storage/client-state/adapter-idb.js +19 -0
  24. package/dist/core/storage/client-state/adapter-memory.d.ts +20 -0
  25. package/dist/core/storage/client-state/adapter-memory.js +44 -0
  26. package/dist/core/storage/client-state/adapter-sql.d.ts +41 -0
  27. package/dist/core/storage/client-state/adapter-sql.js +104 -0
  28. package/dist/core/storage/contact-directory/contacts/_types.d.ts +3 -0
  29. package/dist/core/storage/contact-directory/contacts/_types.js +1 -0
  30. package/dist/core/storage/contact-directory/contacts/adapter-idb.d.ts +3 -0
  31. package/dist/core/storage/contact-directory/contacts/adapter-idb.js +5 -0
  32. package/dist/core/storage/contact-directory/contacts/adapter-memory.d.ts +14 -0
  33. package/dist/core/storage/contact-directory/contacts/adapter-memory.js +32 -0
  34. package/dist/core/storage/contact-directory/contacts/adapter-sql.d.ts +16 -0
  35. package/dist/core/storage/contact-directory/contacts/adapter-sql.js +73 -0
  36. package/dist/core/storage/contact-directory/groups/_types.d.ts +3 -0
  37. package/dist/core/storage/contact-directory/groups/_types.js +1 -0
  38. package/dist/core/storage/contact-directory/groups/adapter-idb.d.ts +3 -0
  39. package/dist/core/storage/contact-directory/groups/adapter-idb.js +5 -0
  40. package/dist/core/storage/contact-directory/groups/adapter-memory.d.ts +14 -0
  41. package/dist/core/storage/contact-directory/groups/adapter-memory.js +32 -0
  42. package/dist/core/storage/contact-directory/groups/adapter-sql.d.ts +16 -0
  43. package/dist/core/storage/contact-directory/groups/adapter-sql.js +71 -0
  44. package/dist/core/storage/idb-adapter.d.ts +21 -0
  45. package/dist/core/storage/idb-adapter.js +107 -0
  46. package/dist/core/storage/index.d.ts +24 -0
  47. package/dist/core/storage/index.js +19 -0
  48. package/dist/core/storage/keystore/_types.d.ts +3 -0
  49. package/dist/core/storage/keystore/_types.js +1 -0
  50. package/dist/core/storage/keystore/adapter-idb.d.ts +3 -0
  51. package/dist/core/storage/keystore/adapter-idb.js +5 -0
  52. package/dist/core/storage/keystore/adapter-memory.d.ts +14 -0
  53. package/dist/core/storage/keystore/adapter-memory.js +32 -0
  54. package/dist/core/storage/keystore/adapter-sql.d.ts +16 -0
  55. package/dist/core/storage/keystore/adapter-sql.js +69 -0
  56. package/dist/core/storage/sql-db-manager.d.ts +13 -0
  57. package/dist/core/storage/sql-db-manager.js +59 -0
  58. package/dist/core/storage/sql-schema.d.ts +10 -0
  59. package/dist/core/storage/sql-schema.js +108 -0
  60. package/dist/core/storage/storage-adapter.d.ts +14 -0
  61. package/dist/core/storage/storage-adapter.js +1 -0
  62. package/dist/index.d.ts +2 -4
  63. package/dist/index.js +2 -4
  64. package/dist/majik-message.d.ts +109 -174
  65. package/dist/majik-message.js +428 -677
  66. package/package.json +4 -5
package/README.md CHANGED
@@ -602,9 +602,9 @@ Once enabled, any page you load will be automatically scanned for quantum-safe e
602
602
  | Typing Indicators | ✓ Supported |
603
603
  | Read Receipts | ✓ Supported |
604
604
  | Message Expiration | ✓ Custom timers available |
605
- | File/Image Sharing | Coming soon |
606
- | Voice Messages | Coming soon |
607
- | Video Calling | Planned |
605
+ | File/Image Sharing | Supported |
606
+ | Voice Messages | Supported |
607
+ | P2P Audio Calling | Supported |
608
608
 
609
609
  ---
610
610
 
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @file client-state-manager.ts
3
+ * @description ClientStateManager — typed read/write interface over a
4
+ * pluggable ClientStateStorageAdapter.
5
+ *
6
+ * Responsibilities:
7
+ * - Async get / set / remove for each well-known client-state key
8
+ * - In-memory cache in front of the adapter (warm via hydrate())
9
+ * - Typed accessors for `accountOrder` and `invoiceDefaults` so callers
10
+ * never touch raw JSON strings
11
+ * - Generic `get` / `set` escape hatch for any future keys
12
+ * - Adapter can be swapped at runtime via setAdapter()
13
+ *
14
+ * Usage:
15
+ * ```ts
16
+ * const stateManager = new ClientStateManager(new IDBClientStateAdapter());
17
+ * await stateManager.hydrate();
18
+ *
19
+ * await stateManager.setAccountOrder(["id1", "id2"]);
20
+ * const order = await stateManager.getAccountOrder(); // ["id1", "id2"]
21
+ * ```
22
+ *
23
+ * MajikBuwizClient owns this instance and calls hydrate() during its own
24
+ * hydrate() pass. Callers should never need to hydrate() again unless the
25
+ * adapter is swapped.
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
+ */
36
+ 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
+ /**
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
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * @file client-state-manager.ts
3
+ * @description ClientStateManager — typed read/write interface over a
4
+ * pluggable ClientStateStorageAdapter.
5
+ *
6
+ * Responsibilities:
7
+ * - Async get / set / remove for each well-known client-state key
8
+ * - In-memory cache in front of the adapter (warm via hydrate())
9
+ * - Typed accessors for `accountOrder` and `invoiceDefaults` so callers
10
+ * never touch raw JSON strings
11
+ * - Generic `get` / `set` escape hatch for any future keys
12
+ * - Adapter can be swapped at runtime via setAdapter()
13
+ *
14
+ * Usage:
15
+ * ```ts
16
+ * const stateManager = new ClientStateManager(new IDBClientStateAdapter());
17
+ * await stateManager.hydrate();
18
+ *
19
+ * await stateManager.setAccountOrder(["id1", "id2"]);
20
+ * const order = await stateManager.getAccountOrder(); // ["id1", "id2"]
21
+ * ```
22
+ *
23
+ * MajikBuwizClient owns this instance and calls hydrate() during its own
24
+ * hydrate() pass. Callers should never need to hydrate() again unless the
25
+ * adapter is swapped.
26
+ */
27
+ import { CLIENT_STATE_KEYS, } from "./storage/client-state/_types";
28
+ import { InMemoryClientStateAdapter } from "./storage/client-state/adapter-memory";
29
+ // ---------------------------------------------------------------------------
30
+ // ClientStateManager
31
+ // ---------------------------------------------------------------------------
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
+ */
40
+ 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 (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);
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
+ // ── 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();
249
+ }
250
+ }
@@ -1,4 +1,3 @@
1
- import { MessageEnvelope } from "../messages/message-envelope";
2
1
  import { MAJIK_API_RESPONSE } from "../types";
3
2
  import { MajikContact, MajikContactData } from "@majikah/majik-contact";
4
3
  import { MajikContactDirectoryData } from "./types";
@@ -30,10 +29,6 @@ export declare class MajikContactDirectory {
30
29
  setMajikahStatus(id: string, status: boolean): MajikContact;
31
30
  isMajikahIdentityChecked(id: string): boolean;
32
31
  isMajikahRegistered(id: string): boolean;
33
- /**
34
- * Checks if a given envelope corresponds to a known contact
35
- */
36
- hasContactForEnvelope(envelope: MessageEnvelope): boolean;
37
32
  toJSON(): Promise<MajikContactDirectoryData>;
38
33
  fromJSON(data: MajikContactDirectoryData): Promise<this>;
39
34
  private assertId;
@@ -155,18 +155,6 @@ export class MajikContactDirectory {
155
155
  throw new MajikContactDirectoryError("Contact not found");
156
156
  return contact.isMajikahRegistered();
157
157
  }
158
- /**
159
- * Checks if a given envelope corresponds to a known contact
160
- */
161
- hasContactForEnvelope(envelope) {
162
- try {
163
- const fingerprint = envelope.extractFingerprint();
164
- return this.hasFingerprint(fingerprint);
165
- }
166
- catch {
167
- return false;
168
- }
169
- }
170
158
  /* ================================
171
159
  * Serialization / Persistence
172
160
  * ================================ */
@@ -47,6 +47,7 @@ export declare class MajikContactGroupManager {
47
47
  * Cleans up the reverse index for all former members.
48
48
  */
49
49
  removeGroup(id: string): MAJIK_API_RESPONSE;
50
+ clear(): this;
50
51
  getGroup(id: string): MajikContactGroup | undefined;
51
52
  getGroupOrThrow(id: string): MajikContactGroup;
52
53
  hasGroup(id: string): boolean;
@@ -116,6 +116,11 @@ export class MajikContactGroupManager {
116
116
  data: originalGroup,
117
117
  };
118
118
  }
119
+ clear() {
120
+ this.groups.clear();
121
+ this.contactGroupIndex.clear();
122
+ return this;
123
+ }
119
124
  getGroup(id) {
120
125
  this.assertGroupId(id);
121
126
  return this.groups.get(id);