@majikah/majik-universal-id-client 0.0.1

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.
@@ -0,0 +1,15 @@
1
+ import { type IDBPDatabase } from "idb";
2
+ export interface MajikIDBSaveData {
3
+ id: string;
4
+ data: Blob;
5
+ savedAt: number;
6
+ }
7
+ interface MajikAutosaveSchema {
8
+ majikdata: MajikIDBSaveData;
9
+ }
10
+ export declare function initDB(name?: string): Promise<IDBPDatabase<MajikAutosaveSchema>>;
11
+ export declare function idbSaveBlob(id: string, data: Blob, name?: string): Promise<void>;
12
+ export declare function idbLoadBlob(id: string, name?: string): Promise<MajikIDBSaveData | undefined>;
13
+ export declare function deleteBlob(id: string, name?: string): Promise<void>;
14
+ export declare function clearAllBlobs(name?: string): Promise<void>;
15
+ export {};
@@ -0,0 +1,44 @@
1
+ // lib/indexedDB.ts
2
+ import { openDB } from "idb";
3
+ let dbPromise;
4
+ export function initDB(name = "default") {
5
+ if (!dbPromise) {
6
+ const dbName = `MajikAutosaveDB_${name}`;
7
+ dbPromise = openDB(dbName, 1, {
8
+ upgrade(db) {
9
+ if (!db.objectStoreNames.contains("majikdata")) {
10
+ db.createObjectStore("majikdata", { keyPath: "id" });
11
+ }
12
+ },
13
+ });
14
+ }
15
+ return dbPromise;
16
+ }
17
+ export async function idbSaveBlob(id, data, name = "default") {
18
+ const db = await initDB(name);
19
+ await db.put("majikdata", { id, data, savedAt: Date.now() });
20
+ }
21
+ export async function idbLoadBlob(id, name = "default") {
22
+ try {
23
+ const db = await initDB(name);
24
+ return await db.get("majikdata", id);
25
+ }
26
+ catch (err) {
27
+ console.error(`Failed to load blob with id "${id}":`, err);
28
+ return undefined;
29
+ }
30
+ }
31
+ export async function deleteBlob(id, name = "default") {
32
+ try {
33
+ const db = await initDB(name);
34
+ return db.delete("majikdata", id);
35
+ }
36
+ catch (err) {
37
+ console.error(`Failed to delete blob with id "${id}":`, err);
38
+ return undefined;
39
+ }
40
+ }
41
+ export async function clearAllBlobs(name = "default") {
42
+ const db = await initDB(name);
43
+ return db.clear("majikdata");
44
+ }
@@ -0,0 +1,16 @@
1
+ interface MajikFileData {
2
+ /** JSON object */
3
+ j: unknown;
4
+ /** STX Timestamp */
5
+ s: string;
6
+ /** Version */
7
+ v: string;
8
+ }
9
+ export declare function importMajikFileData(file: File): Promise<MajikFileData>;
10
+ export declare function loadSavedMajikFileData(file: Blob): Promise<MajikFileData>;
11
+ export declare function autoSaveMajikFileData(json: unknown, version?: string): Blob;
12
+ export declare function exportMajikFileData(json: unknown, name: string, version?: string): void;
13
+ export declare function prepareToBlobFile(data: string): Blob;
14
+ export declare function base64EncodeUtf8(str: string): string;
15
+ export declare function base64DecodeUtf8(base64: string): string;
16
+ export {};
@@ -0,0 +1,153 @@
1
+ import APITranscoder from "./APITranscoder";
2
+ export async function importMajikFileData(file) {
3
+ if (!file)
4
+ throw new Error("Invalid file.");
5
+ if (!file.name.endsWith(".mjkb")) {
6
+ throw new Error("Oops! Only .mjkb files are allowed. Please load a valid MajikFile.");
7
+ }
8
+ try {
9
+ // Step 1: Read as base64 text
10
+ const base64String = (await readBlobFile(file));
11
+ // Step 2: base64 → JSON
12
+ const jsonString = base64DecodeUtf8(base64String);
13
+ const parsedData = JSON.parse(jsonString);
14
+ // Step 3: Decrypt
15
+ const decryptedData = APITranscoder.decryptPayload(parsedData);
16
+ return decryptedData;
17
+ }
18
+ catch (error) {
19
+ console.error("Import Error:", error);
20
+ throw new Error("Failed to import and decrypt .mjkb file.");
21
+ }
22
+ }
23
+ export async function loadSavedMajikFileData(file) {
24
+ try {
25
+ const content = await readBlobFile(file);
26
+ // Step 2: base64 → UTF-8 JSON string
27
+ const jsonString = base64DecodeUtf8(content);
28
+ // Step 3: Parse encrypted payload
29
+ const parsedData = JSON.parse(jsonString);
30
+ const decryptedData = APITranscoder.decryptPayload(parsedData);
31
+ const MajikFileData = decryptedData;
32
+ return MajikFileData;
33
+ }
34
+ catch (error) {
35
+ console.error("Loading Autosaved File Error: ", error);
36
+ throw new Error("Failed to load autosaved .MajikFile file.");
37
+ }
38
+ }
39
+ export function autoSaveMajikFileData(json, version = "1.0.0") {
40
+ const rawData = {
41
+ j: json,
42
+ s: secureTimecode(),
43
+ v: version,
44
+ };
45
+ const generatedRQC = APITranscoder.generateRQC();
46
+ const encryptedData = APITranscoder.encryptPayload(rawData, generatedRQC);
47
+ const dataString = JSON.stringify(encryptedData);
48
+ // 🔐 Convert JSON string to base64
49
+ const base64Encoded = base64EncodeUtf8(dataString);
50
+ const blobData = prepareToBlobFile(base64Encoded);
51
+ return blobData;
52
+ }
53
+ export function exportMajikFileData(json, name, version = "1.0.0") {
54
+ const rawData = {
55
+ j: json,
56
+ s: secureTimecode(),
57
+ v: version,
58
+ };
59
+ const generatedRQC = APITranscoder.generateRQC();
60
+ const encryptedData = APITranscoder.encryptPayload(rawData, generatedRQC);
61
+ const dataString = JSON.stringify(encryptedData);
62
+ // 🔐 Convert JSON string to base64
63
+ const base64Encoded = base64EncodeUtf8(dataString);
64
+ const blobData = prepareToBlobFile(base64Encoded);
65
+ downloadBlob(blobData, "MajikFile", name);
66
+ }
67
+ // Prepare Blob file from various input types
68
+ export function prepareToBlobFile(data) {
69
+ return new Blob([data], { type: "text/plain" });
70
+ }
71
+ // Download Blob file
72
+ function downloadBlob(blob, filetype, fileName) {
73
+ const file = new File([blob], `${fileName}.${filetype}`, { type: blob.type });
74
+ const tryShare = async () => {
75
+ if (typeof navigator !== "undefined" &&
76
+ navigator.canShare &&
77
+ navigator.canShare({ files: [file] })) {
78
+ try {
79
+ await navigator.share({
80
+ files: [file],
81
+ title: fileName,
82
+ text: `Download ${fileName}.${filetype}`,
83
+ });
84
+ return;
85
+ }
86
+ catch (error) {
87
+ console.warn("Share not supported in desktop:", error);
88
+ // Fallback continues
89
+ }
90
+ }
91
+ // Fallback to download
92
+ const url = URL.createObjectURL(blob);
93
+ const a = document.createElement("a");
94
+ a.href = url;
95
+ a.download = `${fileName}.${filetype}`;
96
+ document.body.appendChild(a);
97
+ a.click();
98
+ document.body.removeChild(a);
99
+ URL.revokeObjectURL(url);
100
+ };
101
+ // Ensure it's wrapped in a user-triggered event like a button click
102
+ void tryShare();
103
+ }
104
+ // Read Blob file and decode its content
105
+ function readBlobFile(blob) {
106
+ return new Promise((resolve, reject) => {
107
+ const reader = new FileReader();
108
+ reader.onload = () => resolve(reader.result);
109
+ reader.onerror = () => reject(new Error("Error reading file"));
110
+ reader.readAsText(blob);
111
+ });
112
+ }
113
+ /**
114
+ * Converts a Unix timestamp, Date object, ISO string, or null/undefined into a secure Base64 string.
115
+ * Defaults to the current time if input is null or undefined.
116
+ *
117
+ * @param {(number | Date | string | null | undefined)} input - The time input.
118
+ * @returns {string} A secure Base64 encoded string generated from the timestamp.
119
+ * @throws {Error} Throws an error if the input is not a valid time format.
120
+ */
121
+ function secureTimecode(input) {
122
+ const unixTimestamp = (() => {
123
+ if (input === null || input === undefined)
124
+ return Math.floor(Date.now() / 1000);
125
+ if (typeof input === "number")
126
+ return input;
127
+ if (input instanceof Date)
128
+ return Math.floor(input.getTime() / 1000);
129
+ if (typeof input === "string")
130
+ return Math.floor(new Date(input).getTime() / 1000);
131
+ throw new Error("Invalid input type. Must be Unix timestamp, Date object, or ISO string.");
132
+ })();
133
+ const unixArray = Array.from(String(unixTimestamp), Number);
134
+ const reversedArray = [...unixArray].reverse();
135
+ const invertedArray = [];
136
+ for (let i = 0; i < unixArray.length; i++) {
137
+ invertedArray.push(unixArray[i], reversedArray[i]);
138
+ }
139
+ return btoa(String.fromCharCode(...invertedArray));
140
+ }
141
+ export function base64EncodeUtf8(str) {
142
+ const bytes = new TextEncoder().encode(str);
143
+ let binary = "";
144
+ bytes.forEach((b) => {
145
+ binary += String.fromCharCode(b);
146
+ });
147
+ return btoa(binary);
148
+ }
149
+ export function base64DecodeUtf8(base64) {
150
+ const binary = atob(base64);
151
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
152
+ return new TextDecoder().decode(bytes);
153
+ }
@@ -0,0 +1,18 @@
1
+ import { MnemonicJSON } from "@majikah/majik-key";
2
+ export declare function arrayToBase64(data: Uint8Array): string;
3
+ export declare function base64ToUint8Array(base64: string): Uint8Array;
4
+ export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
5
+ export declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
6
+ export declare function base64ToUtf8(base64: string): string;
7
+ export declare function utf8ToBase64(str: string): string;
8
+ export declare function concatArrayBuffers(a: ArrayBuffer, b: ArrayBuffer): ArrayBuffer;
9
+ export declare function concatUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array;
10
+ /**
11
+ * Converts a space-separated seed phrase string into MnemonicJSON
12
+ */
13
+ export declare function seedToJSON(seed: string, id: string, phrase?: string): MnemonicJSON;
14
+ /**
15
+ * Converts MnemonicJSON into a single space-separated string
16
+ */
17
+ export declare function jsonToSeed(json: MnemonicJSON): string;
18
+ export declare function seedStringToArray(seed: string): string[];
@@ -0,0 +1,80 @@
1
+ /* ================================
2
+ * Utilities
3
+ * ================================ */
4
+ // utils/utilities.ts
5
+ export function arrayToBase64(data) {
6
+ let binary = "";
7
+ const bytes = data;
8
+ const len = bytes.byteLength;
9
+ for (let i = 0; i < len; i++) {
10
+ binary += String.fromCharCode(bytes[i]);
11
+ }
12
+ return btoa(binary);
13
+ }
14
+ export function base64ToUint8Array(base64) {
15
+ return new Uint8Array(base64ToArrayBuffer(base64));
16
+ }
17
+ export function arrayBufferToBase64(buffer) {
18
+ const bytes = new Uint8Array(buffer);
19
+ let binary = "";
20
+ const chunkSize = 0x8000;
21
+ for (let i = 0; i < bytes.length; i += chunkSize) {
22
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
23
+ }
24
+ return btoa(binary);
25
+ }
26
+ export function base64ToArrayBuffer(base64) {
27
+ const binary = atob(base64);
28
+ const bytes = new Uint8Array(binary.length);
29
+ for (let i = 0; i < binary.length; i++) {
30
+ bytes[i] = binary.charCodeAt(i);
31
+ }
32
+ return bytes.buffer;
33
+ }
34
+ export function base64ToUtf8(base64) {
35
+ const buf = base64ToArrayBuffer(base64);
36
+ return new TextDecoder().decode(new Uint8Array(buf));
37
+ }
38
+ export function utf8ToBase64(str) {
39
+ const bytes = new TextEncoder().encode(str);
40
+ return arrayBufferToBase64(bytes.buffer);
41
+ }
42
+ export function concatArrayBuffers(a, b) {
43
+ const tmp = new Uint8Array(a.byteLength + b.byteLength);
44
+ tmp.set(new Uint8Array(a), 0);
45
+ tmp.set(new Uint8Array(b), a.byteLength);
46
+ return tmp.buffer;
47
+ }
48
+ export function concatUint8Arrays(a, b) {
49
+ const out = new Uint8Array(a.byteLength + b.byteLength);
50
+ out.set(a, 0);
51
+ out.set(b, a.byteLength);
52
+ return out;
53
+ }
54
+ /**
55
+ * Converts a space-separated seed phrase string into MnemonicJSON
56
+ */
57
+ export function seedToJSON(seed, id, phrase) {
58
+ return {
59
+ seed: seed
60
+ .trim()
61
+ .split(/\s+/)
62
+ .map((w) => w.toLowerCase())
63
+ .filter(Boolean),
64
+ id,
65
+ phrase,
66
+ };
67
+ }
68
+ /**
69
+ * Converts MnemonicJSON into a single space-separated string
70
+ */
71
+ export function jsonToSeed(json) {
72
+ return json.seed.join(" ");
73
+ }
74
+ export function seedStringToArray(seed) {
75
+ return seed
76
+ .trim()
77
+ .split(/\s+/)
78
+ .map((w) => w.toLowerCase())
79
+ .filter(Boolean);
80
+ }
@@ -0,0 +1,9 @@
1
+ export * from "./majik-universal-id-client";
2
+ export type * from "./core/types";
3
+ export * from "./core/contacts/majik-contact";
4
+ export * from "./core/contacts/majik-contact-directory";
5
+ export * from "./core/crypto/constants";
6
+ export * from "./core/crypto/keystore";
7
+ export * from "./core/utils/APITranscoder";
8
+ export * from "./core/utils/utilities";
9
+ export * from "./core/identity";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./majik-universal-id-client";
2
+ export * from "./core/contacts/majik-contact";
3
+ export * from "./core/contacts/majik-contact-directory";
4
+ export * from "./core/crypto/constants";
5
+ export * from "./core/crypto/keystore";
6
+ export * from "./core/utils/APITranscoder";
7
+ export * from "./core/utils/utilities";
8
+ export * from "./core/identity";