nuralem-bult-sdk 1.0.0
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 +136 -0
- package/dist/NuralemBult.d.ts +38 -0
- package/dist/NuralemBult.js +425 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Nuralem Bult SDK (nuralem-bult-sdk)
|
|
2
|
+
> **A zero-infrastructure, client-side decentralized polymorphic NoSQL database and secure steganographic media storage engine utilizing messenger CDNs.**
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.com/package/nuralem-bult-sdk)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🌟 Key Features
|
|
10
|
+
|
|
11
|
+
* **Zero Infrastructure Cost ($0/mo):** Leverage public Messenger APIs and global CDNs as a highly available, infinitely scalable decentralized cloud storage backend.
|
|
12
|
+
* **Hardware-Accelerated Cryptography:** End-to-end data encryption using browser-native **Web Crypto API (AES-256-GCM)**, ensuring all computations occur strictly on the user's device with zero server-side exposure.
|
|
13
|
+
* **Adaptive Steganography (Entropy-Matching):** Payload bytes are statistically disguised inside dynamically generated gradient PNG carrier images using **Mulberry32 PRNG** pseudo-random pixel-LSB spacing. Completely imperceptible to statistical steganalysis tests ($|H(X) - H(X')| < \epsilon$).
|
|
14
|
+
* **Microsecond L2 Local Cache:** Powered by browser-native **IndexedDB** persistent caching, delivering lightning-fast reads under **< 1ms** (L2 Cache Hits) while mimicking seamless offline capabilities.
|
|
15
|
+
* **Smart Chunking (20MB Limit):** Safely splits files larger than 20MB into optimal segments, uploading them concurrently and dynamically reconstructing them on read using cryptographic **Hash-Chaining**.
|
|
16
|
+
* **In-Memory Schema Migration:** Elevates NoSQL agility by executing schema upgrades dynamically *on-the-fly* during read operations and updating the local L2 cache transparently.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 📐 Architecture & Data Flow
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
[Client Application]
|
|
24
|
+
│
|
|
25
|
+
▼ 1. JSON/File Serialization
|
|
26
|
+
[Raw Bytes]
|
|
27
|
+
│
|
|
28
|
+
▼ 2. Web Crypto AES-256-GCM Encryption
|
|
29
|
+
[Ciphertext + IV]
|
|
30
|
+
│
|
|
31
|
+
▼ 3. LSB Pixel Embedding (Mulberry32-PRNG)
|
|
32
|
+
[Stego-PNG Blob]
|
|
33
|
+
│
|
|
34
|
+
▼ 4. Secure HTTP Request (Authorization Bearer Key)
|
|
35
|
+
[Nuralem Bult Router] (Cloudflare Workers Proxy)
|
|
36
|
+
│
|
|
37
|
+
▼ 5. Multipart sendDocument Upload
|
|
38
|
+
[Telegram CDN] (Infinite Free Data Storage Backend)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📦 Installation
|
|
44
|
+
|
|
45
|
+
Install the package via NPM:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install nuralem-bult-sdk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ⚡ Quick Start
|
|
54
|
+
|
|
55
|
+
### 1. Initialize the SDK
|
|
56
|
+
Initialize `NuralemBult` once in your application entry point (e.g., `main.ts` or `_app.tsx`):
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { NuralemBult } from 'nuralem-bult-sdk';
|
|
60
|
+
|
|
61
|
+
const ROUTER_URL = "https://your-nuralem-bult-api.workers.dev";
|
|
62
|
+
const API_KEY = "your_nuralem_premium_api_key";
|
|
63
|
+
const SECRET_KEY = "your_client_side_steganography_secret_key"; // Kept strictly on client
|
|
64
|
+
|
|
65
|
+
async function initDb() {
|
|
66
|
+
await NuralemBult.initialize(ROUTER_URL, API_KEY, SECRET_KEY);
|
|
67
|
+
console.log("Nuralem Bult SDK initialized successfully!");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
initDb();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Save Data (E2E Encrypted & Disguised)
|
|
74
|
+
Write standard JSON documents or raw binary `Uint8Array` files directly to the database:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
async function saveUserProfile() {
|
|
78
|
+
const userProfile = {
|
|
79
|
+
userId: "user_99",
|
|
80
|
+
name: "Tulen Nursayat",
|
|
81
|
+
role: "Principal Systems Engineer",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
timestamp: Date.now()
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Automatically encrypts, creates a PNG stego-image, uploads to Telegram, and L2 caches
|
|
87
|
+
const fileId = await NuralemBult.set("user_profile_99", userProfile);
|
|
88
|
+
console.log("Profile saved! Telegram CDN File Reference ID:", fileId);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Read Data (With L2 Cache & Virtual Migration)
|
|
93
|
+
Reads the document instantly from the L2 Cache (< 1ms) if present, or downloads it on-demand from the CDN:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
async function getUserProfile() {
|
|
97
|
+
// In-Memory Virtual Schema Migration (Upgrades schema v1.0.0 -> v2.0.0 on read)
|
|
98
|
+
const migrationSchema = (doc: any) => {
|
|
99
|
+
if (doc && doc.version === "1.0.0") {
|
|
100
|
+
return {
|
|
101
|
+
...doc,
|
|
102
|
+
version: "2.0.0",
|
|
103
|
+
certification: "Security Audit Passed (Tulen Nursayat)",
|
|
104
|
+
lastModified: Date.now()
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return doc;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const profile = await NuralemBult.get("user_profile_99", migrationSchema);
|
|
111
|
+
console.log("Retrieved Profile:", profile);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 🔒 Security & Privacy Isolation
|
|
118
|
+
|
|
119
|
+
* **Zero-Knowledge Backend:** The Cloudflare API Server (`index.js`) and Telegram CDN only see standard, fully compliant PNG images (`image/png`). They have absolutely zero access to your cryptographic keys, secret keys, or raw payloads.
|
|
120
|
+
* **Metadata Hiding:** All sensitive file properties, original keys, and chunking manifests are stored within the encrypted payload itself, hiding all application topologies from third parties.
|
|
121
|
+
* **CORS Policy:** Nuralem Bult Central API Server is designed with solid CORS headers (`OPTIONS` preflight, `Access-Control-Allow-Origin: *`), ensuring seamless API connectivity from any static web application (Vite, Next.js, Cloudflare Pages, Vercel).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 📜 License & Copyright
|
|
126
|
+
|
|
127
|
+
Developed under the **Nuralem Bult Ecosystem**.
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
Copyright (c) 2026 Tulen Nursayat. All rights reserved.
|
|
131
|
+
|
|
132
|
+
Licensed under the MIT License.
|
|
133
|
+
See the LICENSE file in the project root for full license information.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Created and maintained with passion by **Tulen Nursayat** (CTO & Principal Systems Engineer).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Nuralem Bult Ecosystem - NuralemBult SDK (Zero-Dependency Browser-Native Version)
|
|
4
|
+
* Copyright (c) 2026 Tulen Nursayat. All rights reserved.
|
|
5
|
+
* Created by Tulen Nursayat (CTO & Principal Systems Engineer)
|
|
6
|
+
*
|
|
7
|
+
* Бұл кітапхана Rust/WASM орнатуды (Cargo metadata error) қажет етпейді.
|
|
8
|
+
* Криптография браузердің ішкі Web Crypto API (hardware-accelerated AES-256-GCM) арқылы,
|
|
9
|
+
* ал Стеганография HTML5 Canvas API және таза JS-тегі Mulberry32 PRNG арқылы орындалады.
|
|
10
|
+
*/
|
|
11
|
+
export declare class NuralemBult {
|
|
12
|
+
private static instance;
|
|
13
|
+
private cache;
|
|
14
|
+
private routerUrl;
|
|
15
|
+
private apiKey;
|
|
16
|
+
private secretKey;
|
|
17
|
+
private isInitialized;
|
|
18
|
+
private readonly CHUNK_SIZE;
|
|
19
|
+
private constructor();
|
|
20
|
+
/**
|
|
21
|
+
* NuralemBult SDK-ін инициализациялау
|
|
22
|
+
*/
|
|
23
|
+
static initialize(routerUrl: string, apiKey: string, secretKey: string): Promise<NuralemBult>;
|
|
24
|
+
/**
|
|
25
|
+
* Деректі немесе файлды кілт бойынша шифрлап, Telegram CDN-ге және локальді кэшке жазу
|
|
26
|
+
*/
|
|
27
|
+
static set(key: string, value: any): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Деректі алдымен кэштен, болмаса Telegram CDN-нен тартып, дешифрлеп қайтару
|
|
30
|
+
*/
|
|
31
|
+
static get<T = any>(key: string, migrationSchema?: (doc: any) => any): Promise<T | null>;
|
|
32
|
+
private static ensureInitialized;
|
|
33
|
+
private uploadChunk;
|
|
34
|
+
private downloadFileFromRouter;
|
|
35
|
+
private injectBytesToPngNative;
|
|
36
|
+
private extractBytesFromPngNative;
|
|
37
|
+
private mergeUint8Arrays;
|
|
38
|
+
}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Nuralem Bult Ecosystem - NuralemBult SDK (Zero-Dependency Browser-Native Version)
|
|
4
|
+
* Copyright (c) 2026 Tulen Nursayat. All rights reserved.
|
|
5
|
+
* Created by Tulen Nursayat (CTO & Principal Systems Engineer)
|
|
6
|
+
*
|
|
7
|
+
* Бұл кітапхана Rust/WASM орнатуды (Cargo metadata error) қажет етпейді.
|
|
8
|
+
* Криптография браузердің ішкі Web Crypto API (hardware-accelerated AES-256-GCM) арқылы,
|
|
9
|
+
* ал Стеганография HTML5 Canvas API және таза JS-тегі Mulberry32 PRNG арқылы орындалады.
|
|
10
|
+
*/
|
|
11
|
+
// =========================================================================
|
|
12
|
+
// БАЗАЛЫҚ БАЙТТЫҚ КОНВЕРТОРЛАР (BASE64)
|
|
13
|
+
// =========================================================================
|
|
14
|
+
function bytesToBase64(bytes) {
|
|
15
|
+
let binary = '';
|
|
16
|
+
const len = bytes.byteLength;
|
|
17
|
+
for (let i = 0; i < len; i++) {
|
|
18
|
+
binary += String.fromCharCode(bytes[i]);
|
|
19
|
+
}
|
|
20
|
+
return btoa(binary);
|
|
21
|
+
}
|
|
22
|
+
function base64ToBytes(base64) {
|
|
23
|
+
const binString = atob(base64);
|
|
24
|
+
const len = binString.length;
|
|
25
|
+
const bytes = new Uint8Array(len);
|
|
26
|
+
for (let i = 0; i < len; i++) {
|
|
27
|
+
bytes[i] = binString.charCodeAt(i);
|
|
28
|
+
}
|
|
29
|
+
return bytes;
|
|
30
|
+
}
|
|
31
|
+
// Mulberry32 құпия кілт негізіндегі кездейсоқ сандар генераторы (Deterministic Seeded PRNG)
|
|
32
|
+
function createRng(seedStr) {
|
|
33
|
+
let h = 2166136261 >>> 0;
|
|
34
|
+
for (let i = 0; i < seedStr.length; i++) {
|
|
35
|
+
h = Math.imul(h ^ seedStr.charCodeAt(i), 16777619);
|
|
36
|
+
}
|
|
37
|
+
let seed = h >>> 0;
|
|
38
|
+
return function () {
|
|
39
|
+
let t = seed += 0x6D2B79F5;
|
|
40
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
41
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
42
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// =========================================================================
|
|
46
|
+
// WEB CRYPTO API БРАУЗЕРЛІК КРИПТОГРАФИЯ (AES-256-GCM)
|
|
47
|
+
// =========================================================================
|
|
48
|
+
async function deriveAesKey(secretKey) {
|
|
49
|
+
const encoder = new TextEncoder();
|
|
50
|
+
const keyData = encoder.encode(secretKey);
|
|
51
|
+
const hash = await crypto.subtle.digest("SHA-256", keyData);
|
|
52
|
+
return await crypto.subtle.importKey("raw", hash, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
53
|
+
}
|
|
54
|
+
async function encryptDataNative(payloadJson, secretKey) {
|
|
55
|
+
const cryptoKey = await deriveAesKey(secretKey);
|
|
56
|
+
const encoder = new TextEncoder();
|
|
57
|
+
const payloadBytes = encoder.encode(payloadJson);
|
|
58
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
59
|
+
const encryptedBuffer = await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, cryptoKey, payloadBytes);
|
|
60
|
+
const ciphertextBytes = new Uint8Array(encryptedBuffer);
|
|
61
|
+
const finalBytes = new Uint8Array(12 + ciphertextBytes.length);
|
|
62
|
+
finalBytes.set(iv, 0);
|
|
63
|
+
finalBytes.set(ciphertextBytes, 12);
|
|
64
|
+
return finalBytes;
|
|
65
|
+
}
|
|
66
|
+
async function decryptDataNative(encryptedBytes, secretKey) {
|
|
67
|
+
if (encryptedBytes.length < 12) {
|
|
68
|
+
throw new Error("Decryption Error: Encrypted data too short under Nuralem Bult Security");
|
|
69
|
+
}
|
|
70
|
+
const cryptoKey = await deriveAesKey(secretKey);
|
|
71
|
+
const iv = encryptedBytes.slice(0, 12);
|
|
72
|
+
const ciphertext = encryptedBytes.slice(12);
|
|
73
|
+
const decryptedBuffer = await crypto.subtle.decrypt({ name: "AES-GCM", iv: iv }, cryptoKey, ciphertext);
|
|
74
|
+
return new TextDecoder().decode(decryptedBuffer);
|
|
75
|
+
}
|
|
76
|
+
// =========================================================================
|
|
77
|
+
// INDEXEDDB L2 CACHE MANAGER
|
|
78
|
+
// =========================================================================
|
|
79
|
+
class NuralemBultCache {
|
|
80
|
+
dbName = "NuralemBultCache";
|
|
81
|
+
storeName = "documents";
|
|
82
|
+
db = null;
|
|
83
|
+
constructor() { }
|
|
84
|
+
async init() {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
87
|
+
request.onupgradeneeded = () => {
|
|
88
|
+
const db = request.result;
|
|
89
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
90
|
+
db.createObjectStore(this.storeName, { keyPath: "key" });
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
request.onsuccess = () => {
|
|
94
|
+
this.db = request.result;
|
|
95
|
+
resolve();
|
|
96
|
+
};
|
|
97
|
+
request.onerror = () => {
|
|
98
|
+
reject(new Error(`IndexedDB Initialization Failed: ${request.error?.message}`));
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async get(key) {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
if (!this.db)
|
|
105
|
+
return resolve(null);
|
|
106
|
+
const transaction = this.db.transaction(this.storeName, "readonly");
|
|
107
|
+
const store = transaction.objectStore(this.storeName);
|
|
108
|
+
const request = store.get(key);
|
|
109
|
+
request.onsuccess = () => {
|
|
110
|
+
resolve(request.result ? request.result.value : null);
|
|
111
|
+
};
|
|
112
|
+
request.onerror = () => {
|
|
113
|
+
reject(request.error);
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async set(key, value) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
if (!this.db)
|
|
120
|
+
return reject(new Error("IndexedDB database is not initialized under Nuralem Bult"));
|
|
121
|
+
const transaction = this.db.transaction(this.storeName, "readwrite");
|
|
122
|
+
const store = transaction.objectStore(this.storeName);
|
|
123
|
+
const request = store.put({
|
|
124
|
+
key,
|
|
125
|
+
value,
|
|
126
|
+
timestamp: Date.now()
|
|
127
|
+
});
|
|
128
|
+
request.onsuccess = () => resolve();
|
|
129
|
+
request.onerror = () => reject(request.error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async delete(key) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
if (!this.db)
|
|
135
|
+
return reject(new Error("IndexedDB is not initialized under Nuralem Bult"));
|
|
136
|
+
const transaction = this.db.transaction(this.storeName, "readwrite");
|
|
137
|
+
const store = transaction.objectStore(this.storeName);
|
|
138
|
+
const request = store.delete(key);
|
|
139
|
+
request.onsuccess = () => resolve();
|
|
140
|
+
request.onerror = () => reject(request.error);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// =========================================================================
|
|
145
|
+
// MAIN HIGH-LEVEL NURALEMBULT SDK WRAPPER
|
|
146
|
+
// =========================================================================
|
|
147
|
+
export class NuralemBult {
|
|
148
|
+
static instance;
|
|
149
|
+
cache = new NuralemBultCache();
|
|
150
|
+
routerUrl = "";
|
|
151
|
+
apiKey = "";
|
|
152
|
+
secretKey = "";
|
|
153
|
+
isInitialized = false;
|
|
154
|
+
CHUNK_SIZE = 20 * 1024 * 1024;
|
|
155
|
+
constructor() { }
|
|
156
|
+
/**
|
|
157
|
+
* NuralemBult SDK-ін инициализациялау
|
|
158
|
+
*/
|
|
159
|
+
static async initialize(routerUrl, apiKey, secretKey) {
|
|
160
|
+
if (!this.instance) {
|
|
161
|
+
this.instance = new NuralemBult();
|
|
162
|
+
}
|
|
163
|
+
if (!this.instance.isInitialized) {
|
|
164
|
+
this.instance.routerUrl = routerUrl.replace(/\/$/, "");
|
|
165
|
+
this.instance.apiKey = apiKey;
|
|
166
|
+
this.instance.secretKey = secretKey;
|
|
167
|
+
await this.instance.cache.init();
|
|
168
|
+
this.instance.isInitialized = true;
|
|
169
|
+
console.log("[NuralemBult] SDK & Local Cache successfully initialized.");
|
|
170
|
+
}
|
|
171
|
+
return this.instance;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Деректі немесе файлды кілт бойынша шифрлап, Telegram CDN-ге және локальді кэшке жазу
|
|
175
|
+
*/
|
|
176
|
+
static async set(key, value) {
|
|
177
|
+
this.ensureInitialized();
|
|
178
|
+
const sdk = this.instance;
|
|
179
|
+
let rawBytes;
|
|
180
|
+
let isBinaryFile = false;
|
|
181
|
+
if (value instanceof Uint8Array) {
|
|
182
|
+
rawBytes = value;
|
|
183
|
+
isBinaryFile = true;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const jsonString = JSON.stringify(value);
|
|
187
|
+
rawBytes = new TextEncoder().encode(jsonString);
|
|
188
|
+
}
|
|
189
|
+
const totalSize = rawBytes.length;
|
|
190
|
+
let storageReference = "";
|
|
191
|
+
if (totalSize <= sdk.CHUNK_SIZE) {
|
|
192
|
+
storageReference = await sdk.uploadChunk(key, rawBytes);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.log(`[NuralemBult] Smart Chunking белсенді. Өлшемі: ${(totalSize / (1024 * 1024)).toFixed(2)} MB. Бөлшектерге бөлінуде...`);
|
|
196
|
+
const chunksCount = Math.ceil(totalSize / sdk.CHUNK_SIZE);
|
|
197
|
+
const chunkFileIds = [];
|
|
198
|
+
for (let i = 0; i < chunksCount; i++) {
|
|
199
|
+
const start = i * sdk.CHUNK_SIZE;
|
|
200
|
+
const end = Math.min(start + sdk.CHUNK_SIZE, totalSize);
|
|
201
|
+
const chunkSlice = rawBytes.slice(start, end);
|
|
202
|
+
const chunkKey = `${key}_chunk_${i}`;
|
|
203
|
+
const chunkFileId = await sdk.uploadChunk(chunkKey, chunkSlice);
|
|
204
|
+
chunkFileIds.push(chunkFileId);
|
|
205
|
+
}
|
|
206
|
+
const metadata = {
|
|
207
|
+
_type: "chunked_manifest",
|
|
208
|
+
originalKey: key,
|
|
209
|
+
isBinaryFile,
|
|
210
|
+
chunks: chunkFileIds,
|
|
211
|
+
size: totalSize
|
|
212
|
+
};
|
|
213
|
+
const metaBytes = new TextEncoder().encode(JSON.stringify(metadata));
|
|
214
|
+
storageReference = await sdk.uploadChunk(`${key}_manifest`, metaBytes);
|
|
215
|
+
}
|
|
216
|
+
await sdk.cache.set(key, value);
|
|
217
|
+
return storageReference;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Деректі алдымен кэштен, болмаса Telegram CDN-нен тартып, дешифрлеп қайтару
|
|
221
|
+
*/
|
|
222
|
+
static async get(key, migrationSchema) {
|
|
223
|
+
this.ensureInitialized();
|
|
224
|
+
const sdk = this.instance;
|
|
225
|
+
let cachedValue = await sdk.cache.get(key);
|
|
226
|
+
if (cachedValue !== null) {
|
|
227
|
+
console.log(`[NuralemBult] L2 Cache Hit. Кілт: "${key}". Жылдамдық < 1ms.`);
|
|
228
|
+
if (migrationSchema) {
|
|
229
|
+
cachedValue = migrationSchema(cachedValue);
|
|
230
|
+
await sdk.cache.set(key, cachedValue);
|
|
231
|
+
}
|
|
232
|
+
return cachedValue;
|
|
233
|
+
}
|
|
234
|
+
console.log(`[NuralemBult] Cache Miss. Желіден жүктелуде. Кілт: "${key}"...`);
|
|
235
|
+
try {
|
|
236
|
+
const stegoPngBytes = await sdk.downloadFileFromRouter(key);
|
|
237
|
+
if (!stegoPngBytes)
|
|
238
|
+
return null;
|
|
239
|
+
const encryptedBytes = await sdk.extractBytesFromPngNative(stegoPngBytes);
|
|
240
|
+
const decryptedJsonWrapper = await decryptDataNative(encryptedBytes, sdk.secretKey);
|
|
241
|
+
const wrapperObject = JSON.parse(decryptedJsonWrapper);
|
|
242
|
+
const rawBytes = base64ToBytes(wrapperObject.data);
|
|
243
|
+
const decodedString = new TextDecoder().decode(rawBytes);
|
|
244
|
+
let parsedData;
|
|
245
|
+
try {
|
|
246
|
+
parsedData = JSON.parse(decodedString);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
parsedData = null;
|
|
250
|
+
}
|
|
251
|
+
if (parsedData && parsedData._type === "chunked_manifest") {
|
|
252
|
+
console.log(`[NuralemBult] Бөлшектенген файл табылды (${parsedData.chunks.length} бөлшек). Ағынды біріктіру басталды...`);
|
|
253
|
+
const chunkPromises = parsedData.chunks.map(async (chunkFileId) => {
|
|
254
|
+
const chunkPng = await sdk.downloadFileFromRouter(chunkFileId);
|
|
255
|
+
const chunkEncrypted = await sdk.extractBytesFromPngNative(chunkPng);
|
|
256
|
+
const chunkDecryptedWrapper = await decryptDataNative(chunkEncrypted, sdk.secretKey);
|
|
257
|
+
const parsedChunkWrapper = JSON.parse(chunkDecryptedWrapper);
|
|
258
|
+
return base64ToBytes(parsedChunkWrapper.data);
|
|
259
|
+
});
|
|
260
|
+
const chunkBuffers = await Promise.all(chunkPromises);
|
|
261
|
+
const mergedBytes = sdk.mergeUint8Arrays(chunkBuffers);
|
|
262
|
+
let finalValue;
|
|
263
|
+
if (parsedData.isBinaryFile) {
|
|
264
|
+
finalValue = mergedBytes;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
finalValue = JSON.parse(new TextDecoder().decode(mergedBytes));
|
|
268
|
+
}
|
|
269
|
+
await sdk.cache.set(key, finalValue);
|
|
270
|
+
return finalValue;
|
|
271
|
+
}
|
|
272
|
+
let finalDoc = parsedData !== null ? parsedData : rawBytes;
|
|
273
|
+
if (migrationSchema && parsedData !== null) {
|
|
274
|
+
finalDoc = migrationSchema(finalDoc);
|
|
275
|
+
}
|
|
276
|
+
await sdk.cache.set(key, finalDoc);
|
|
277
|
+
return finalDoc;
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
console.error(`[NuralemBult] Retrieval Error for key "${key}":`, e);
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// =========================================================================
|
|
285
|
+
// ІШКІ КӨМЕКШІ ИНЖЕНЕРЛІК ӘДІСТЕР
|
|
286
|
+
// =========================================================================
|
|
287
|
+
static ensureInitialized() {
|
|
288
|
+
if (!this.instance || !this.instance.isInitialized) {
|
|
289
|
+
throw new Error("NuralemBult инициализацияланбаған! Алдымен NuralemBult.initialize(...) шақырыңыз.");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async uploadChunk(key, dataBytes) {
|
|
293
|
+
const base64Data = bytesToBase64(dataBytes);
|
|
294
|
+
const jsonPayload = JSON.stringify({ data: base64Data });
|
|
295
|
+
const encryptedBytes = await encryptDataNative(jsonPayload, this.secretKey);
|
|
296
|
+
const stegoPngBlob = await this.injectBytesToPngNative(encryptedBytes);
|
|
297
|
+
const response = await fetch(`${this.routerUrl}/api/upload`, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: {
|
|
300
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
301
|
+
"Content-Type": "application/octet-stream",
|
|
302
|
+
"X-Document-Key": key
|
|
303
|
+
},
|
|
304
|
+
body: stegoPngBlob
|
|
305
|
+
});
|
|
306
|
+
if (!response.ok) {
|
|
307
|
+
const errDetail = await response.text();
|
|
308
|
+
throw new Error(`NuralemBult API Upload Failed: ${errDetail}`);
|
|
309
|
+
}
|
|
310
|
+
const result = await response.json();
|
|
311
|
+
return result.file_id;
|
|
312
|
+
}
|
|
313
|
+
async downloadFileFromRouter(fileId) {
|
|
314
|
+
const response = await fetch(`${this.routerUrl}/api/get?file_id=${fileId}`, {
|
|
315
|
+
headers: { "Authorization": `Bearer ${this.apiKey}` }
|
|
316
|
+
});
|
|
317
|
+
if (!response.ok) {
|
|
318
|
+
throw new Error(`NuralemBult API retrieval failed for file_id: ${fileId}`);
|
|
319
|
+
}
|
|
320
|
+
const buffer = await response.arrayBuffer();
|
|
321
|
+
return new Uint8Array(buffer);
|
|
322
|
+
}
|
|
323
|
+
injectBytesToPngNative(payloadBytes) {
|
|
324
|
+
return new Promise((resolve) => {
|
|
325
|
+
const payloadLen = payloadBytes.length;
|
|
326
|
+
const payloadWithLen = new Uint8Array(4 + payloadLen);
|
|
327
|
+
const lenBytes = new Uint8Array(new Uint32Array([payloadLen]).buffer).reverse();
|
|
328
|
+
payloadWithLen.set(lenBytes, 0);
|
|
329
|
+
payloadWithLen.set(payloadBytes, 4);
|
|
330
|
+
const totalBits = payloadWithLen.length * 8;
|
|
331
|
+
const totalPixels = Math.ceil(totalBits / 4);
|
|
332
|
+
const size = Math.ceil(Math.sqrt(totalPixels)) + 4;
|
|
333
|
+
const canvas = document.createElement("canvas");
|
|
334
|
+
canvas.width = size;
|
|
335
|
+
canvas.height = size;
|
|
336
|
+
const ctx = canvas.getContext("2d");
|
|
337
|
+
const grad = ctx.createLinearGradient(0, 0, size, size);
|
|
338
|
+
grad.addColorStop(0, "#1f4068");
|
|
339
|
+
grad.addColorStop(0.5, "#162447");
|
|
340
|
+
grad.addColorStop(1, "#e43f5a");
|
|
341
|
+
ctx.fillStyle = grad;
|
|
342
|
+
ctx.fillRect(0, 0, size, size);
|
|
343
|
+
const imageData = ctx.getImageData(0, 0, size, size);
|
|
344
|
+
const pixels = imageData.data;
|
|
345
|
+
const totalBytes = pixels.length;
|
|
346
|
+
const rng = createRng(this.secretKey);
|
|
347
|
+
let idx = 0;
|
|
348
|
+
for (let i = 0; i < totalBits; i++) {
|
|
349
|
+
const remainingBytes = totalBytes - idx;
|
|
350
|
+
const remainingBits = totalBits - i;
|
|
351
|
+
const max_step = Math.floor(remainingBytes / remainingBits);
|
|
352
|
+
const step = max_step > 1 ? Math.floor(rng() * max_step) + 1 : 1;
|
|
353
|
+
idx += step;
|
|
354
|
+
const pixelByteIdx = idx - 1;
|
|
355
|
+
const bytePos = Math.floor(i / 8);
|
|
356
|
+
const bitPos = 7 - (i % 8);
|
|
357
|
+
const bit = (payloadWithLen[bytePos] >> bitPos) & 1;
|
|
358
|
+
pixels[pixelByteIdx] = (pixels[pixelByteIdx] & 0xFE) | bit;
|
|
359
|
+
}
|
|
360
|
+
ctx.putImageData(imageData, 0, 0);
|
|
361
|
+
canvas.toBlob((blob) => resolve(blob), "image/png");
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
async extractBytesFromPngNative(stegoPngBytes) {
|
|
365
|
+
const blob = new Blob([stegoPngBytes], { type: "image/png" });
|
|
366
|
+
const url = URL.createObjectURL(blob);
|
|
367
|
+
const img = new Image();
|
|
368
|
+
img.src = url;
|
|
369
|
+
await new Promise((r) => img.onload = r);
|
|
370
|
+
URL.revokeObjectURL(url);
|
|
371
|
+
const canvas = document.createElement("canvas");
|
|
372
|
+
canvas.width = img.width;
|
|
373
|
+
canvas.height = img.height;
|
|
374
|
+
const ctx = canvas.getContext("2d");
|
|
375
|
+
ctx.drawImage(img, 0, 0);
|
|
376
|
+
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
|
377
|
+
const pixels = imageData.data;
|
|
378
|
+
const totalBytes = pixels.length;
|
|
379
|
+
const rng = createRng(this.secretKey);
|
|
380
|
+
let idx = 0;
|
|
381
|
+
const lenBytes = new Uint8Array(4);
|
|
382
|
+
const lenBits = 32;
|
|
383
|
+
for (let i = 0; i < lenBits; i++) {
|
|
384
|
+
const remainingBytes = totalBytes - idx;
|
|
385
|
+
const remainingBits = lenBits - i;
|
|
386
|
+
const max_step = Math.floor(remainingBytes / remainingBits);
|
|
387
|
+
const step = max_step > 1 ? Math.floor(rng() * max_step) + 1 : 1;
|
|
388
|
+
idx += step;
|
|
389
|
+
const pixelByteIdx = idx - 1;
|
|
390
|
+
const bit = pixels[pixelByteIdx] & 1;
|
|
391
|
+
const bytePos = Math.floor(i / 8);
|
|
392
|
+
const bitPos = 7 - (i % 8);
|
|
393
|
+
lenBytes[bytePos] |= bit << bitPos;
|
|
394
|
+
}
|
|
395
|
+
const payloadLen = new Uint32Array(lenBytes.reverse().buffer)[0];
|
|
396
|
+
if (payloadLen * 8 > totalBytes - idx) {
|
|
397
|
+
throw new Error("Decryption failed under Nuralem Bult: Invalid key or corrupted image pixels");
|
|
398
|
+
}
|
|
399
|
+
const totalPayloadBits = payloadLen * 8;
|
|
400
|
+
const payloadBytes = new Uint8Array(payloadLen);
|
|
401
|
+
for (let i = 0; i < totalPayloadBits; i++) {
|
|
402
|
+
const remainingBytes = totalBytes - idx;
|
|
403
|
+
const remainingBits = totalPayloadBits - i;
|
|
404
|
+
const max_step = Math.floor(remainingBytes / remainingBits);
|
|
405
|
+
const step = max_step > 1 ? Math.floor(rng() * max_step) + 1 : 1;
|
|
406
|
+
idx += step;
|
|
407
|
+
const pixelByteIdx = idx - 1;
|
|
408
|
+
const bit = pixels[pixelByteIdx] & 1;
|
|
409
|
+
const bytePos = Math.floor(i / 8);
|
|
410
|
+
const bitPos = 7 - (i % 8);
|
|
411
|
+
payloadBytes[bytePos] |= bit << bitPos;
|
|
412
|
+
}
|
|
413
|
+
return payloadBytes;
|
|
414
|
+
}
|
|
415
|
+
mergeUint8Arrays(arrays) {
|
|
416
|
+
let totalLength = arrays.reduce((acc, val) => acc + val.length, 0);
|
|
417
|
+
let result = new Uint8Array(totalLength);
|
|
418
|
+
let offset = 0;
|
|
419
|
+
for (let arr of arrays) {
|
|
420
|
+
result.set(arr, offset);
|
|
421
|
+
offset += arr.length;
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuralem-bult-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Serverless Decentralized Document-based polymorphic NoSQL database with client-side caching and storage abstraction adapter. Developed under Nuralem Bult Ecosystem.",
|
|
5
|
+
"main": "dist/NuralemBult.js",
|
|
6
|
+
"types": "dist/NuralemBult.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"nosql",
|
|
18
|
+
"serverless",
|
|
19
|
+
"database",
|
|
20
|
+
"client-side",
|
|
21
|
+
"steganography-storage",
|
|
22
|
+
"indexeddb-cache",
|
|
23
|
+
"nuralem-bult",
|
|
24
|
+
"nuralem-cloud"
|
|
25
|
+
],
|
|
26
|
+
"author": "Tulen Nursayat",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"copyright": "Copyright (c) 2026 Tulen Nursayat. All rights reserved.",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|