@majikah/majik-message 0.1.4 → 0.1.6

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.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **Majik Message** is a secure messaging platform built on cryptographic identity. Your account *is* your encryption keys—no phone numbers, no passwords, just your 12-word seed phrase and complete privacy.
7
7
 
8
- ![npm](https://img.shields.io/npm/v/@majikah/majik-message) ![npm downloads](https://img.shields.io/npm/dm/@majikah/majik-message) ![npm bundle size](https://img.shields.io/bundlephobia/min/%40thezelijah%2Fmajik-message) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
8
+ ![npm](https://img.shields.io/npm/v/@majikah/majik-message) ![npm downloads](https://img.shields.io/npm/dm/@majikah/majik-message) ![npm bundle size](https://img.shields.io/bundlephobia/min/%40majikah%2Fmajik-message) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
9
9
 
10
10
 
11
11
 
@@ -534,7 +534,7 @@ All Majikah products share the same core principles: cryptographic identity, zer
534
534
  ### [Majik Key](https://majikah.solutions/sdk/majik-key)
535
535
  **Majik Key** is a seed phrase account library for creating, managing, and parsing mnemonic-based cryptographic accounts (Majik Keys). Generate deterministic key pairs from BIP39 seed phrases with simple, developer-friendly APIs.
536
536
 
537
- ![npm](https://img.shields.io/npm/v/@majikah/majik-key) ![npm downloads](https://img.shields.io/npm/dm/@majikah/majik-key) ![npm bundle size](https://img.shields.io/bundlephobia/min/%40thezelijah%2Fmajik-key) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
537
+ ![npm](https://img.shields.io/npm/v/@majikah/majik-key) ![npm downloads](https://img.shields.io/npm/dm/@majikah/majik-key) ![npm bundle size](https://img.shields.io/bundlephobia/min/%40majikah%2Fmajik-key) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
538
538
 
539
539
  [Read Docs](https://majikah.solutions/sdk/majik-key/docs)
540
540
  [Official Repository](https://github.com/Majikah/majik-key)
@@ -56,6 +56,7 @@ export declare class MajikMessageMail {
56
56
  get account(): MajikMessageAccountID;
57
57
  get sender(): MajikMessagePublicKey;
58
58
  get recipients(): readonly MajikMessagePublicKey[];
59
+ get participants(): readonly MajikMessagePublicKey[];
59
60
  get timestamp(): Date;
60
61
  get metadata(): Readonly<MailMetadata>;
61
62
  get hash(): string;
@@ -133,6 +134,12 @@ export declare class MajikMessageMail {
133
134
  * @returns true if successfully marked, false if already read
134
135
  */
135
136
  markAsRead(recipientPublicKey: MajikMessagePublicKey): boolean;
137
+ /**
138
+ * Marks this mail as unread by removing a recipient from the read_by list.
139
+ * @param recipientPublicKey - The public key of the recipient marking as unread
140
+ * @returns true if successfully unmarked, false if wasn't read
141
+ */
142
+ markAsUnread(recipientPublicKey: MajikMessagePublicKey): boolean;
136
143
  /**
137
144
  * Checks if a specific user has read this mail.
138
145
  */
@@ -77,6 +77,9 @@ export class MajikMessageMail {
77
77
  get recipients() {
78
78
  return [...this._recipients];
79
79
  }
80
+ get participants() {
81
+ return [...this._recipients, this._sender];
82
+ }
80
83
  get timestamp() {
81
84
  return new Date(this._timestamp);
82
85
  }
@@ -554,6 +557,39 @@ export class MajikMessageMail {
554
557
  throw new MailOperationError(`Failed to mark as read: ${error instanceof Error ? error.message : "Unknown error"}`);
555
558
  }
556
559
  }
560
+ /**
561
+ * Marks this mail as unread by removing a recipient from the read_by list.
562
+ * @param recipientPublicKey - The public key of the recipient marking as unread
563
+ * @returns true if successfully unmarked, false if wasn't read
564
+ */
565
+ markAsUnread(recipientPublicKey) {
566
+ try {
567
+ if (!recipientPublicKey ||
568
+ typeof recipientPublicKey !== "string" ||
569
+ recipientPublicKey.trim() === "") {
570
+ throw new MailValidationError("Recipient public key must be a non-empty string");
571
+ }
572
+ const trimmedKey = recipientPublicKey.trim();
573
+ // Verify recipient is in recipients list
574
+ if (!this._recipients.includes(trimmedKey)) {
575
+ throw new MailOperationError(`User ${trimmedKey} is not a recipient of this mail`);
576
+ }
577
+ // Check if not in read_by list (idempotent)
578
+ const readIndex = this._readBy.indexOf(trimmedKey);
579
+ if (readIndex === -1) {
580
+ return false; // Already unread
581
+ }
582
+ // Remove from read_by array
583
+ this._readBy.splice(readIndex, 1);
584
+ return true; // Successfully marked as unread
585
+ }
586
+ catch (error) {
587
+ if (error instanceof MajikMailError) {
588
+ throw error;
589
+ }
590
+ throw new MailOperationError(`Failed to mark as unread: ${error instanceof Error ? error.message : "Unknown error"}`);
591
+ }
592
+ }
557
593
  /**
558
594
  * Checks if a specific user has read this mail.
559
595
  */
@@ -13,7 +13,7 @@ export interface ThreadMetadata {
13
13
  messageCount?: number;
14
14
  }
15
15
  export interface DeletionApproval {
16
- publicKey: string;
16
+ publicKey: MajikMessagePublicKey;
17
17
  approvalHash: string;
18
18
  timestamp: Date;
19
19
  }
@@ -49,6 +49,7 @@ export interface MajikMessageThreadSummary {
49
49
  unread_count: number;
50
50
  has_unread: boolean;
51
51
  starred: boolean;
52
+ subject?: string;
52
53
  }
53
54
  export interface MajikMessageThreadJSON {
54
55
  id: MajikMessageThreadID;
@@ -112,9 +113,10 @@ export declare class MajikMessageThread {
112
113
  private static generateApprovalHash;
113
114
  validate(): boolean;
114
115
  close(): void;
115
- requestDeletion(publicKey: string): void;
116
+ hasDeletionApproval(publicKey: MajikMessagePublicKey): boolean;
117
+ requestDeletion(publicKey: MajikMessagePublicKey): void;
116
118
  private updateDeletionStatus;
117
- revokeDeletionRequest(publicKey: string): void;
119
+ revokeDeletionRequest(publicKey: MajikMessagePublicKey): void;
118
120
  canBeDeleted(): boolean;
119
121
  getDeletionProgress(): {
120
122
  approved: number;
@@ -137,11 +139,11 @@ export declare class MajikMessageThread {
137
139
  missingApprovals: string[];
138
140
  duplicateApprovals: string[];
139
141
  };
140
- updateMetadata(metadata: Partial<ThreadMetadata>): void;
142
+ updateMetadata(metadata: Partial<ThreadMetadata>): this;
141
143
  toJSON(): MajikMessageThreadJSON;
142
144
  static fromJSON(json: MajikMessageThreadJSON | string): MajikMessageThread;
143
- isOwner(publicKey: string): boolean;
144
- isParticipant(publicKey: string): boolean;
145
+ isOwner(publicKey: MajikMessagePublicKey): boolean;
146
+ isParticipant(publicKey: MajikMessagePublicKey): boolean;
145
147
  toString(): string;
146
148
  /**
147
149
  * Deduplicates and sorts participants to ensure consistent ordering
@@ -266,6 +266,11 @@ export class MajikMessageThread {
266
266
  }
267
267
  }
268
268
  // ==================== Deletion Approval System ====================
269
+ hasDeletionApproval(publicKey) {
270
+ if (!this.isParticipant(publicKey))
271
+ return false;
272
+ return this._deletionApprovals.some((approval) => approval.publicKey === publicKey);
273
+ }
269
274
  requestDeletion(publicKey) {
270
275
  try {
271
276
  // Validate public key is a participant
@@ -276,9 +281,7 @@ export class MajikMessageThread {
276
281
  if (this._status === ThreadStatus.CLOSED) {
277
282
  throw new OperationNotAllowedError("Cannot request deletion of a closed thread");
278
283
  }
279
- // Check if already approved
280
- const existingApproval = this._deletionApprovals.find((approval) => approval.publicKey === publicKey);
281
- if (existingApproval) {
284
+ if (this.hasDeletionApproval(publicKey)) {
282
285
  throw new OperationNotAllowedError("This participant has already approved deletion");
283
286
  }
284
287
  // Create approval
@@ -451,6 +454,7 @@ export class MajikMessageThread {
451
454
  ...metadata,
452
455
  lastActivity: new Date().toISOString(),
453
456
  };
457
+ return this;
454
458
  }
455
459
  catch (error) {
456
460
  if (error instanceof MajikThreadError) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-message",
3
3
  "type": "module",
4
4
  "description": "Encrypt and decrypt messages on any website or platform. Secure chats with keypairs and seed-based accounts. Open source.",
5
- "version": "0.1.4",
5
+ "version": "0.1.6",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",