@majikah/majik-message 0.3.5 → 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 +7 -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 +5 -6
@@ -2,235 +2,143 @@ 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 { MessageEnvelope } from "../messages/message-envelope";
6
5
  import { MajikContactManagerJSON } from "./types";
7
- /**
8
- * Unified facade over MajikContactDirectory and MajikContactGroupManager.
9
- *
10
- * Responsibilities:
11
- * - Owns both the directory and the group manager as a single cohesive unit
12
- * - Proxies all directory methods so MajikMessage call sites need only change
13
- * `contactDirectory` → `contacts` with no logic changes
14
- * - Wires lifecycle hooks automatically so callers can never forget them:
15
- * • removeContact() → always calls groups.handleContactRemoved()
16
- * • blockContact() → always syncs the Blocked system group
17
- * • unblockContact() → always syncs the Blocked system group
18
- * - Exposes the group manager via `.group` for all group-specific operations
19
- * - Serializes both directory and groups into one unified payload for
20
- * MajikMessage.toJSON() / MajikMessage.fromJSON()
21
- *
22
- * Construction:
23
- * - Pass nothing → fresh directory + fresh group manager (new session)
24
- * - Pass a directory → wraps it, creates a fresh group manager bound to it
25
- * - Pass both → fully restores a prior session (used by fromJSON)
26
- */
6
+ import { MajikContactStorageAdapter } from "../storage/contact-directory/contacts/_types";
7
+ import { MajikContactGroupStorageAdapter } from "../storage/contact-directory/groups/_types";
8
+ export interface MajikContactManagerAdapters {
9
+ contacts?: MajikContactStorageAdapter;
10
+ groups?: MajikContactGroupStorageAdapter;
11
+ }
27
12
  export declare class MajikContactManager {
28
13
  private readonly directory;
29
14
  private readonly groupManager;
30
- constructor(directory?: MajikContactDirectory, groupManager?: MajikContactGroupManager);
15
+ private _contactAdapter;
16
+ private _groupAdapter;
17
+ constructor(directory?: MajikContactDirectory, groupManager?: MajikContactGroupManager, adapters?: MajikContactManagerAdapters);
18
+ get contactAdapter(): MajikContactStorageAdapter;
19
+ get groupAdapter(): MajikContactGroupStorageAdapter;
31
20
  /**
32
- * Direct access to the full MajikContactGroupManager API.
33
- * Use for all group-specific operations:
34
- * manager.group.addToFavorites(contactId)
35
- * manager.group.createGroup(id, name)
36
- * manager.group.getContactsInGroup(groupId)
37
- * etc.
21
+ * Swap both adapters at runtime. Does NOT migrate data.
22
+ *
23
+ * Migration pattern:
24
+ * ```ts
25
+ * const snap = await manager.toJSON();
26
+ * manager.setAdapters({ contacts: new IDBContactAdapter(), groups: new IDBGroupAdapter() });
27
+ * await manager.hydrate(); // warms from new (empty) adapters
28
+ * await manager.bulkRestoreFromJSON(snap); // writes old data into new adapters
29
+ * ```
30
+ */
31
+ setAdapters(adapters: MajikContactManagerAdapters): void;
32
+ /**
33
+ * Load all contacts and groups from the adapters into the in-memory
34
+ * directory and group manager. Call once after construction (or after
35
+ * swapping adapters).
36
+ *
37
+ * Restoration order:
38
+ * 1. Contacts — must come first so groups can validate member existence.
39
+ * 2. Groups — restored via groupManager.fromJSON() which rebuilds the
40
+ * reverse index and re-bootstraps system groups.
41
+ * 3. Orphan pruning — any group member ID not present in the restored
42
+ * directory is silently removed (guards against data drift).
38
43
  */
39
- get group(): MajikContactGroupManager;
44
+ hydrate(): Promise<void>;
40
45
  /**
41
- * Direct access to the underlying MajikContactDirectory.
42
- * Prefer the proxied methods on this class over accessing the directory
43
- * directly — they keep group state in sync automatically.
46
+ * Persists a single contact to the adapter (called after every mutating
47
+ * operation that affects a contact's serialized form).
44
48
  */
45
- get directory_(): MajikContactDirectory;
46
- addContact(contact: MajikContact): this;
47
- addContacts(contacts: MajikContact[]): this;
49
+ private persistContact;
48
50
  /**
49
- * Removes a contact from the directory and automatically removes them
50
- * from every group they belong to via the group manager hook.
51
- * The two operations are always atomic from the caller's perspective.
51
+ * Persists a single group to the adapter.
52
52
  */
53
- removeContact(id: string): MAJIK_API_RESPONSE;
54
- getContact(id: string): MajikContact | undefined;
55
- getContactByFingerprint(fingerprint: string): MajikContact | undefined;
56
- getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
57
- hasContact(id: string): boolean;
58
- hasFingerprint(fingerprint: string): boolean;
59
- hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
60
- listContacts(sortedByLabel?: boolean, majikahOnly?: boolean): MajikContact[];
61
- updateContactMeta(id: string, meta: Partial<MajikContactData["meta"]>): MajikContact;
53
+ private persistGroup;
62
54
  /**
63
- * Blocks a contact on the directory AND adds them to the system Blocked
64
- * group — both sides are always kept in sync.
55
+ * Adds a contact to the directory and persists it to the adapter.
65
56
  */
66
- blockContact(id: string): MajikContact;
57
+ addContact(contact: MajikContact): Promise<this>;
67
58
  /**
68
- * Unblocks a contact on the directory AND removes them from the system
69
- * Blocked group — both sides are always kept in sync.
59
+ * Adds multiple contacts atomically adapter write uses bulkSave.
70
60
  */
71
- unblockContact(id: string): MajikContact;
72
- setMajikahStatus(id: string, status: boolean): MajikContact;
73
- isMajikahRegistered(id: string): boolean;
74
- isMajikahIdentityChecked(id: string): boolean;
75
- hasContactForEnvelope(envelope: MessageEnvelope): boolean;
61
+ addContacts(contacts: MajikContact[]): Promise<this>;
76
62
  /**
77
- * Creates and registers a new user-defined group.
78
- * Throws if a group with the same ID already exists.
63
+ * Removes a contact from the directory, all groups, and the adapter.
79
64
  */
80
- createGroup(id: string, name: string, meta?: Partial<Omit<MajikContactGroupMeta, "name">>, initialMemberIds?: string[]): MajikContactGroup;
65
+ removeContact(id: string): Promise<MAJIK_API_RESPONSE>;
81
66
  /**
82
- * Registers an already-constructed MajikContactGroup instance.
83
- * Throws if a group with the same ID already exists.
67
+ * Updates contact metadata and persists the change.
84
68
  */
85
- addGroup(group: MajikContactGroup): this;
69
+ updateContactMeta(id: string, meta: Partial<MajikContactData["meta"]>): Promise<MajikContact>;
86
70
  /**
87
- * Removes a user group by ID.
88
- * System groups (Favorites, Blocked) cannot be deleted.
71
+ * Blocks a contact and persists both the contact and the Blocked group.
89
72
  */
90
- removeGroup(id: string): MAJIK_API_RESPONSE;
73
+ blockContact(id: string): Promise<MajikContact>;
91
74
  /**
92
- * Returns a group by ID, or undefined if not found.
75
+ * Unblocks a contact and persists both the contact and the Blocked group.
93
76
  */
94
- getGroup(id: string): MajikContactGroup | undefined;
77
+ unblockContact(id: string): Promise<MajikContact>;
78
+ setMajikahStatus(id: string, status: boolean): Promise<MajikContact>;
95
79
  /**
96
- * Returns a group by ID. Throws if not found.
80
+ * Clears all contacts and groups from both the in-memory stores and adapters.
97
81
  */
82
+ clear(): Promise<this>;
83
+ getContact(id: string): MajikContact | undefined;
84
+ getContactByFingerprint(fingerprint: string): MajikContact | undefined;
85
+ getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
86
+ hasContact(id: string): boolean;
87
+ hasFingerprint(fingerprint: string): boolean;
88
+ hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
89
+ listContacts(sortedByLabel?: boolean, majikahOnly?: boolean): MajikContact[];
90
+ isMajikahRegistered(id: string): boolean;
91
+ isMajikahIdentityChecked(id: string): boolean;
92
+ get group(): MajikContactGroupManager;
93
+ get directory_(): MajikContactDirectory;
94
+ createGroup(id: string, name: string, meta?: Partial<Omit<MajikContactGroupMeta, "name">>, initialMemberIds?: string[]): Promise<MajikContactGroup>;
95
+ addGroup(group: MajikContactGroup): Promise<this>;
96
+ removeGroup(id: string): Promise<MAJIK_API_RESPONSE>;
97
+ getGroup(id: string): MajikContactGroup | undefined;
98
98
  getGroupOrThrow(id: string): MajikContactGroup;
99
- /**
100
- * Returns true if a group with the given ID exists.
101
- */
102
99
  hasGroup(id: string): boolean;
103
- /**
104
- * Returns all groups.
105
- *
106
- * @param includeSystem Include system groups (Favorites, Blocked). Default: true.
107
- * @param sortedByName Sort results alphabetically by group name. Default: false.
108
- */
109
100
  listGroups(includeSystem?: boolean, sortedByName?: boolean): MajikContactGroup[];
110
- /**
111
- * Returns only user-created groups (excludes Favorites and Blocked).
112
- * Sorted alphabetically by name.
113
- */
114
101
  listUserGroups(sortedByName?: boolean): MajikContactGroup[];
115
- /**
116
- * Returns only system groups (Favorites and Blocked).
117
- */
118
102
  listSystemGroups(): MajikContactGroup[];
119
- /**
120
- * Updates mutable metadata on a group (name, description).
121
- * Name is locked on system groups — will throw if attempted.
122
- */
123
- updateGroupMeta(id: string, meta: Partial<Pick<MajikContactGroupMeta, "name" | "description" | "color">>): MajikContactGroup;
124
- /**
125
- * Adds a contact to a group.
126
- * Validates the contact exists in the directory.
127
- * If the group is the system Blocked group, also calls contact.block().
128
- * Throws if the contact is already a member — use addContactToGroupIfAbsent for idempotent.
129
- */
130
- addContactToGroup(groupId: string, contactId: string): MajikContactGroup;
131
- /**
132
- * Idempotent variant — does not throw if the contact is already a member.
133
- */
134
- addContactToGroupIfAbsent(groupId: string, contactId: string): MajikContactGroup;
135
- /**
136
- * Adds multiple contacts to a group in one call (all-or-nothing).
137
- */
138
- addContactsToGroup(groupId: string, contactIds: string[]): MajikContactGroup;
139
- /**
140
- * Removes a contact from a group.
141
- * If the group is the system Blocked group, also calls contact.unblock().
142
- * Throws if the contact is not a member — use removeContactFromGroupIfPresent for idempotent.
143
- */
144
- removeContactFromGroup(groupId: string, contactId: string): MajikContactGroup;
145
- /**
146
- * Idempotent variant — does not throw if the contact is not a member.
147
- */
148
- removeContactFromGroupIfPresent(groupId: string, contactId: string): MajikContactGroup;
149
- /**
150
- * Moves a contact from one group to another atomically.
151
- * Throws if the contact is not a member of the source group.
152
- */
153
- moveContactBetweenGroups(contactId: string, fromGroupId: string, toGroupId: string): void;
154
- /**
155
- * Returns all hydrated MajikContact instances in the given group.
156
- * Contacts removed from the directory since last save are silently skipped.
157
- */
103
+ updateGroupMeta(id: string, meta: Partial<Pick<MajikContactGroupMeta, "name" | "description" | "color">>): Promise<MajikContactGroup>;
104
+ addContactToGroup(groupId: string, contactId: string): Promise<MajikContactGroup>;
105
+ addContactToGroupIfAbsent(groupId: string, contactId: string): Promise<MajikContactGroup>;
106
+ addContactsToGroup(groupId: string, contactIds: string[]): Promise<MajikContactGroup>;
107
+ removeContactFromGroup(groupId: string, contactId: string): Promise<MajikContactGroup>;
108
+ removeContactFromGroupIfPresent(groupId: string, contactId: string): Promise<MajikContactGroup>;
109
+ moveContactBetweenGroups(contactId: string, fromGroupId: string, toGroupId: string): Promise<void>;
158
110
  getContactsInGroup(groupId: string): MajikContact[];
159
- /**
160
- * Returns hydrated contacts in the group, sorted by label (or ID if no label).
161
- */
162
111
  getContactsInGroupSorted(groupId: string): MajikContact[];
163
- /**
164
- * Returns true if the contact is a member of the given group.
165
- */
166
112
  isContactInGroup(groupId: string, contactId: string): boolean;
167
- /**
168
- * Returns all groups the contact belongs to.
169
- */
170
113
  getGroupsForContact(contactId: string): MajikContactGroup[];
171
- /**
172
- * Returns all group IDs the contact belongs to.
173
- */
174
114
  getGroupIdsForContact(contactId: string): string[];
175
- /**
176
- * Adds the contact to the Favorites group (idempotent).
177
- */
178
- addToFavorites(contactId: string): MajikContactGroup;
179
- /**
180
- * Removes the contact from the Favorites group (idempotent).
181
- */
182
- removeFromFavorites(contactId: string): MajikContactGroup;
183
- /**
184
- * Returns true if the contact is in the Favorites group.
185
- */
115
+ addToFavorites(contactId: string): Promise<MajikContactGroup>;
116
+ removeFromFavorites(contactId: string): Promise<MajikContactGroup>;
186
117
  isFavorite(contactId: string): boolean;
187
- /**
188
- * Returns true if the contact is in the Blocked group.
189
- */
190
118
  isContactBlocked(contactId: string): boolean;
191
- /**
192
- * Returns the Favorites system group instance.
193
- */
194
119
  getFavoritesGroup(): MajikContactGroup;
195
- /**
196
- * Returns the Blocked system group instance.
197
- */
198
120
  getBlockedGroup(): MajikContactGroup;
199
- /**
200
- * Returns all contacts in the Favorites group as hydrated MajikContact instances.
201
- */
202
121
  getFavoriteContacts(): MajikContact[];
203
- /**
204
- * Returns all contacts in the Blocked group as hydrated MajikContact instances.
205
- */
206
122
  getBlockedContacts(): MajikContact[];
207
- /**
208
- * Clears both the directory and all group memberships.
209
- * System groups are preserved (re-bootstrapped by the group manager).
210
- */
211
- clear(): this;
212
- /**
213
- * Serializes both the directory and all groups into a single unified payload.
214
- * This is what MajikMessage.toJSON() should persist.
215
- */
123
+ exportContactAsJSON(contactId: string): Promise<string | null>;
124
+ exportContactAsString(contactId: string): Promise<string | null>;
125
+ importContactFromJSON(jsonStr: string): Promise<MAJIK_API_RESPONSE>;
126
+ importContactFromString(base64Str: string): Promise<MAJIK_API_RESPONSE>;
127
+ exportContactCompressed(contact: MajikContact): Promise<string>;
128
+ importContactCompressed(base64Str: string): Promise<MajikContact>;
216
129
  toJSON(): Promise<MajikContactManagerJSON>;
217
130
  /**
218
- * Restores a MajikContactManager from a unified serialized payload.
219
- *
220
- * Restoration order:
221
- * 1. Restore the directory (contacts + crypto keys)
222
- * 2. Restore groups via the group manager
223
- * 3. Silently strip any group member IDs that no longer exist in the
224
- * restored directory (orphan pruning) — guards against data drift
225
- * between directory and group state across serialization rounds
226
- *
227
- * @param data The payload produced by toJSON().
131
+ * Restore from a JSON snapshot into the current adapters.
132
+ * Writes all contacts and groups through to the adapters.
133
+ * Used after setAdapters() to migrate data into a new store.
228
134
  */
229
- static fromJSON(data: MajikContactManagerJSON): Promise<MajikContactManager>;
135
+ bulkRestoreFromJSON(data: MajikContactManagerJSON): Promise<void>;
136
+ static fromJSON(data: MajikContactManagerJSON, adapters?: MajikContactManagerAdapters): Promise<MajikContactManager>;
230
137
  /**
231
- * Walks every group and removes any member ID not present in the directory.
232
- * Operates directly on the group instances no re-serialization needed.
138
+ * Persists every group currently in the group manager to the adapter.
139
+ * Used after bulk contact removal where multiple groups may be affected.
233
140
  */
141
+ private _persistAllGroups;
234
142
  private static pruneOrphanedMembers;
235
143
  private assertGroupManagerInstance;
236
144
  }