@majikah/majik-file 0.0.10 → 0.0.11

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.
@@ -1,6 +1,8 @@
1
1
  export type MajikMessagePublicKey = string;
2
2
  export type FileContext = "user_upload" | "chat_attachment" | "chat_image" | "thread_attachment";
3
3
  export type StorageType = "permanent" | "temporary";
4
+ /** Allowed TTLs for temporary files in days. Maps 1:1 to R2 lifecycle prefixes. */
5
+ export type TempFileDuration = 1 | 2 | 3 | 5 | 7 | 15;
4
6
  /**
5
7
  * The file owner's full identity.
6
8
  * Carries both keys — public for encryption, secret for decryption.
@@ -185,9 +187,9 @@ export interface CreateOptions {
185
187
  */
186
188
  bypassSizeLimit?: boolean;
187
189
  /**
188
- * ISO-8601 expiry datetime. Required when isTemporary = true.
190
+ * Temporary file duration in days. Required when isTemporary = true.
189
191
  */
190
- expiresAt?: string;
192
+ expiresAt?: TempFileDuration;
191
193
  /** Associate this file with a chat message (mutually exclusive with threadMessageId). */
192
194
  chatMessageId?: string;
193
195
  /** Associate this file with a thread message (mutually exclusive with chatMessageId). */
@@ -11,7 +11,7 @@
11
11
  * - Human-readable file size formatting
12
12
  * - Expiry helpers
13
13
  */
14
- import type { DecodedMjkb, MajikFileRecipient, MjkbPayload } from "./types";
14
+ import type { DecodedMjkb, MajikFileRecipient, MjkbPayload, TempFileDuration } from "./types";
15
15
  export declare function arrayToBase64(data: Uint8Array): string;
16
16
  export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
17
17
  export declare function base64ToUint8Array(base64: string): Uint8Array;
@@ -37,9 +37,9 @@ export declare function buildPermanentR2Key(userId: string, fileHash: string): s
37
37
  /**
38
38
  * Build an R2 object key for a temporary / public file.
39
39
  * Objects under this prefix are auto-deleted by the bucket lifecycle policy.
40
- * files/public/<userId>_<fileHash>.mjkb
40
+ * files/public/15/<userId>_<fileHash>.mjkb
41
41
  */
42
- export declare function buildTemporaryR2Key(userId: string, fileHash: string): string;
42
+ export declare function buildTemporaryR2Key(userId: string, fileHash: string, duration?: TempFileDuration): string;
43
43
  /**
44
44
  * Build an R2 object key for an encrypted WebP chat image.
45
45
  *
@@ -97,7 +97,7 @@ export declare function isExpired(expiresAt: string | null): boolean;
97
97
  * Build a default expiry ISO string for temporary files.
98
98
  * @param days Days from now. Defaults to 15 (matching the R2 lifecycle policy).
99
99
  */
100
- export declare function buildExpiryDate(days?: number): string;
100
+ export declare function buildExpiryDate(days?: TempFileDuration): string;
101
101
  /**
102
102
  * Deduplicate a recipient list and strip the owner's own key.
103
103
  *
@@ -83,10 +83,10 @@ export function buildPermanentR2Key(userId, fileHash) {
83
83
  /**
84
84
  * Build an R2 object key for a temporary / public file.
85
85
  * Objects under this prefix are auto-deleted by the bucket lifecycle policy.
86
- * files/public/<userId>_<fileHash>.mjkb
86
+ * files/public/15/<userId>_<fileHash>.mjkb
87
87
  */
88
- export function buildTemporaryR2Key(userId, fileHash) {
89
- return `${R2_PREFIX.TEMPORARY}/${userId}_${fileHash}.mjkb`;
88
+ export function buildTemporaryR2Key(userId, fileHash, duration = 15) {
89
+ return `${R2_PREFIX.TEMPORARY}/${duration}/${userId}_${fileHash}.mjkb`;
90
90
  }
91
91
  /**
92
92
  * Build an R2 object key for an encrypted WebP chat image.
@@ -1,4 +1,4 @@
1
- import type { MajikFileJSON, CreateOptions, MajikFileIdentity, MajikFileStats, FileContext, StorageType } from "./core/types";
1
+ import type { MajikFileJSON, CreateOptions, MajikFileIdentity, MajikFileStats, FileContext, StorageType, TempFileDuration } from "./core/types";
2
2
  /**
3
3
  * MajikFile
4
4
  * ----------------
@@ -181,17 +181,19 @@ export declare class MajikFile {
181
181
  * @throws MajikFileError when switching to temporary without an expiresAt,
182
182
  * or if the instance has no userId / fileHash yet.
183
183
  */
184
- setStorageType(type: StorageType, expiresAt: string | null): void;
184
+ setStorageType(type: StorageType, expiresAt: string | null, duration?: TempFileDuration): void;
185
185
  /**
186
186
  * Switch to permanent storage. Clears any expiry date and updates the R2 key.
187
187
  */
188
188
  setPermanent(): void;
189
189
  /**
190
- * Switch to temporary storage with an optional TTL.
190
+ * Switch to temporary storage with a typed TTL duration.
191
+ * The duration determines both the R2 prefix bucket and the expiry date.
191
192
  *
192
- * @param days Days until expiry. Defaults to 15 to match R2 lifecycle policy.
193
+ * @param duration Days until expiry. Must be one of: 1 | 2 | 3 | 5 | 7 | 15.
194
+ * Defaults to 15 to match the R2 lifecycle policy.
193
195
  */
194
- setTemporary(days?: number): void;
196
+ setTemporary(duration?: TempFileDuration): void;
195
197
  /**
196
198
  * Serialise metadata to a plain object matching the `majik_files` Supabase table.
197
199
  * The encrypted binary (_binary) is intentionally excluded.
@@ -306,7 +308,7 @@ export declare class MajikFile {
306
308
  * Build a default ISO-8601 expiry date for temporary files.
307
309
  * @param days Days from now. Defaults to 15 (R2 lifecycle policy).
308
310
  */
309
- static buildExpiryDate(days?: number): string;
311
+ static buildExpiryDate(days?: TempFileDuration): string;
310
312
  /** Format bytes as a human-readable string (e.g. "4.2 MB"). */
311
313
  static formatBytes(bytes: number): string;
312
314
  /**
@@ -214,7 +214,7 @@ export class MajikFile {
214
214
  * @throws MajikFileError on validation or crypto failure
215
215
  */
216
216
  static async create(options) {
217
- const { data, identity, context, recipients = [], originalName = null, mimeType: rawMimeType = null, isTemporary = false, isShared = false, id = generateUUID(), bypassSizeLimit = false, expiresAt = null, chatMessageId = null, threadMessageId = null, conversationId = null, userId, } = options;
217
+ const { data, identity, context, recipients = [], originalName = null, mimeType: rawMimeType = null, isTemporary = false, isShared = false, id = generateUUID(), bypassSizeLimit = false, expiresAt = 15, chatMessageId = null, threadMessageId = null, conversationId = null, userId, } = options;
218
218
  // ── Input validation ─────────────────────────────────────────────────
219
219
  if (!data)
220
220
  throw MajikFileError.invalidInput("data is required");
@@ -365,7 +365,7 @@ export class MajikFile {
365
365
  r2Key = buildChatImageR2Key(conversationId, userId, fileHash);
366
366
  }
367
367
  else if (isTemporary) {
368
- r2Key = buildTemporaryR2Key(userId, fileHash);
368
+ r2Key = buildTemporaryR2Key(userId, fileHash, expiresAt); // default TTL at creation time
369
369
  }
370
370
  else {
371
371
  r2Key = buildPermanentR2Key(userId, fileHash);
@@ -388,7 +388,7 @@ export class MajikFile {
388
388
  chat_message_id: chatMessageId,
389
389
  thread_message_id: threadMessageId,
390
390
  conversation_id: conversationId,
391
- expires_at: expiresAt,
391
+ expires_at: buildExpiryDate(expiresAt),
392
392
  timestamp: now,
393
393
  last_update: now,
394
394
  };
@@ -565,7 +565,7 @@ export class MajikFile {
565
565
  * @throws MajikFileError when switching to temporary without an expiresAt,
566
566
  * or if the instance has no userId / fileHash yet.
567
567
  */
568
- setStorageType(type, expiresAt) {
568
+ setStorageType(type, expiresAt, duration = 15) {
569
569
  if (!["permanent", "temporary"].includes(type)) {
570
570
  throw MajikFileError.invalidInput(`setStorageType: type must be "permanent" or "temporary" (got "${type}")`);
571
571
  }
@@ -573,13 +573,11 @@ export class MajikFile {
573
573
  throw MajikFileError.invalidInput("setStorageType: expiresAt is required when switching to temporary. " +
574
574
  "Use setTemporary(days?) instead.");
575
575
  }
576
- // Rebuild R2 key — context is preserved, only the storage tier changes.
577
- // chat_image files are context-scoped and must never be rekeyed here.
578
576
  if (this._context === "chat_image") {
579
577
  throw MajikFileError.invalidInput("setStorageType: chat_image files are conversation-scoped and cannot change storage type.");
580
578
  }
581
579
  const newR2Key = type === "temporary"
582
- ? buildTemporaryR2Key(this._userId, this._fileHash)
580
+ ? buildTemporaryR2Key(this._userId, this._fileHash, duration)
583
581
  : buildPermanentR2Key(this._userId, this._fileHash);
584
582
  this._storageType = type;
585
583
  this._expiresAt = type === "temporary" ? expiresAt : null;
@@ -593,12 +591,14 @@ export class MajikFile {
593
591
  this.setStorageType("permanent", null);
594
592
  }
595
593
  /**
596
- * Switch to temporary storage with an optional TTL.
594
+ * Switch to temporary storage with a typed TTL duration.
595
+ * The duration determines both the R2 prefix bucket and the expiry date.
597
596
  *
598
- * @param days Days until expiry. Defaults to 15 to match R2 lifecycle policy.
597
+ * @param duration Days until expiry. Must be one of: 1 | 2 | 3 | 5 | 7 | 15.
598
+ * Defaults to 15 to match the R2 lifecycle policy.
599
599
  */
600
- setTemporary(days = 15) {
601
- this.setStorageType("temporary", MajikFile.buildExpiryDate(days));
600
+ setTemporary(duration = 15) {
601
+ this.setStorageType("temporary", MajikFile.buildExpiryDate(duration), duration);
602
602
  }
603
603
  // ── SERIALISATION ─────────────────────────────────────────────────────────
604
604
  /**
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-file",
3
3
  "type": "module",
4
4
  "description": "Majik File is the core cryptographic engine for secure file handling in the Majikah ecosystem. It provides a post-quantum secure \"MJKB\" format designed for file encryption, multi-recipient key encapsulation, and transparent compression using NIST-standardized algorithms.",
5
- "version": "0.0.10",
5
+ "version": "0.0.11",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",