kaiwen-core-js 0.1.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 +108 -0
- package/dist/app.d.ts +20 -0
- package/dist/app.js +46 -0
- package/dist/core/exceptions.d.ts +9 -0
- package/dist/core/exceptions.js +24 -0
- package/dist/core/pipeline.d.ts +10 -0
- package/dist/core/pipeline.js +48 -0
- package/dist/crypto/engine.d.ts +10 -0
- package/dist/crypto/engine.js +80 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +34 -0
- package/dist/models/enums.d.ts +8 -0
- package/dist/models/enums.js +13 -0
- package/dist/models/migration.d.ts +7 -0
- package/dist/models/migration.js +22 -0
- package/dist/models/record.d.ts +12 -0
- package/dist/models/record.js +19 -0
- package/dist/models/tracer.d.ts +4 -0
- package/dist/models/tracer.js +9 -0
- package/dist/storage/assets.d.ts +17 -0
- package/dist/storage/assets.js +52 -0
- package/dist/storage/db.d.ts +12 -0
- package/dist/storage/db.js +19 -0
- package/dist/storage/vault.d.ts +7 -0
- package/dist/storage/vault.js +25 -0
- package/dist/sync/network.d.ts +8 -0
- package/dist/sync/network.js +33 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.js +12 -0
- package/dist/utils/tracer.d.ts +5 -0
- package/dist/utils/tracer.js +16 -0
- package/dist/utils/validator.d.ts +3 -0
- package/dist/utils/validator.js +11 -0
- package/package.json +21 -0
- package/src/app.ts +60 -0
- package/src/core/exceptions.ts +20 -0
- package/src/core/pipeline.ts +57 -0
- package/src/crypto/engine.ts +105 -0
- package/src/index.ts +14 -0
- package/src/models/enums.ts +9 -0
- package/src/models/migration.ts +18 -0
- package/src/models/record.ts +19 -0
- package/src/models/tracer.ts +6 -0
- package/src/storage/assets.ts +63 -0
- package/src/storage/db.ts +24 -0
- package/src/storage/vault.ts +23 -0
- package/src/sync/network.ts +28 -0
- package/src/utils/logger.ts +8 -0
- package/src/utils/tracer.ts +13 -0
- package/src/utils/validator.ts +7 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Kaiwen Core JS (kaiwen-core-js)
|
|
2
|
+
|
|
3
|
+
The official TypeScript/JavaScript SDK for the Kaiwen Ecosystem. This library provides a zero-knowledge, end-to-end encrypted architecture for modern web, Node.js, and React Native applications.
|
|
4
|
+
|
|
5
|
+
It is 100% interoperable with the Python `kaiwen-core` SDK, allowing seamless, cross-platform synchronization between devices.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Zero-Knowledge Encryption**: All payloads and media assets are encrypted locally on the device before they even touch the network.
|
|
10
|
+
- **Cross-Platform Cryptography**: PBKDF2 (200k iterations) and HKDF derivations for master and app-isolated keys. Fully compatible with Python's `cryptography.hazmat` stack.
|
|
11
|
+
- **AES-256-GCM**: Military-grade authenticated encryption.
|
|
12
|
+
- **Logical Clock Conflict Resolution**: Decentralized, timestamp-agnostic conflict resolution across devices.
|
|
13
|
+
- **Chunked Asset Engine**: Securely encrypt and decrypt massive media files (videos, photos) in 1MB chunks without running out of RAM.
|
|
14
|
+
- **Dependency Inversion**: Easily inject your own Storage Adapters (e.g., SQLite, AsyncStorage, IndexedDB).
|
|
15
|
+
- **TypeScript First**: 100% strictly typed.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install kaiwen-core-js
|
|
23
|
+
```
|
|
24
|
+
*(Requires Node.js v18+ or a modern browser environment. WebCrypto API is required.)*
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### 1. Initialization and Login
|
|
31
|
+
|
|
32
|
+
Initialize the SDK by providing an App ID, the API URL of the Kaiwen Server, and your storage adapter.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { KaiwenApp, MemoryStorage } from "kaiwen-core-js";
|
|
36
|
+
|
|
37
|
+
// Initialize the Facade
|
|
38
|
+
const storage = new MemoryStorage(); // Or implement IKWStorage for SQLite/IndexedDB
|
|
39
|
+
const app = new KaiwenApp(
|
|
40
|
+
"core.kaiwen.notes", // App ID (Isolated Encryption Domain)
|
|
41
|
+
"https://api.kaiwen.cloud", // Kaiwen API Server
|
|
42
|
+
"your_api_key_here", // Server API Key
|
|
43
|
+
storage
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Login (Derives master key and isolated app key using PBKDF2 + HKDF)
|
|
47
|
+
await app.login("apple banana cherry date elderberry fig grape hazelnut ice juice kiwi lemon");
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Saving Encrypted Records
|
|
51
|
+
|
|
52
|
+
Data is encrypted *before* it is saved to the local database queue.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// Save an end-to-end encrypted note
|
|
56
|
+
const record = await app.saveRecord("user_123", {
|
|
57
|
+
title: "My Secret Plan",
|
|
58
|
+
content: "This is fully encrypted using AES-256-GCM."
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log("Locally saved record ID:", record.id);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Pushing to the Cloud
|
|
65
|
+
|
|
66
|
+
Once records are saved locally, push the pending queue to the Kaiwen Server.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const pushedCount = await app.pushSync();
|
|
70
|
+
console.log(`Successfully synced ${pushedCount} records to the cloud.`);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Pulling from the Cloud
|
|
74
|
+
|
|
75
|
+
Pull changes made by other devices (e.g., a Python desktop app or an iOS device). The SDK will automatically handle decryption and logical clock conflict resolution.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const pulledCount = await app.pullSync();
|
|
79
|
+
console.log(`Fetched ${pulledCount} new or updated records.`);
|
|
80
|
+
|
|
81
|
+
// Decrypt and read the latest state
|
|
82
|
+
const allRecords = await storage.getAllRecords();
|
|
83
|
+
for (const r of allRecords) {
|
|
84
|
+
const decryptedPayload = await app.getDecryptedRecord(r.id);
|
|
85
|
+
console.log("Decrypted Data:", decryptedPayload);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## The Kaiwen Ecosystem Architecture
|
|
92
|
+
|
|
93
|
+
This library implements the precise directory mapping and modularity of the Python `kaiwen-core` SDK:
|
|
94
|
+
|
|
95
|
+
- `/core`: Exceptions and pipeline flows.
|
|
96
|
+
- `/crypto`: Cryptographic engine (WebCrypto API).
|
|
97
|
+
- `/models`: Database schemas, enums, and migration engines.
|
|
98
|
+
- `/storage`: Vault (Key Management), Interfaces, and Chunked Asset Engine.
|
|
99
|
+
- `/sync`: Network dispatchers.
|
|
100
|
+
- `/utils`: Logging and telemetry.
|
|
101
|
+
|
|
102
|
+
## Security Notice
|
|
103
|
+
|
|
104
|
+
- **Never hardcode mnemonics.**
|
|
105
|
+
- **Ensure TLS (HTTPS)** is used when communicating with the `kaiwen_server` to protect metadata (even though payloads are E2E encrypted).
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
© 2026 Kaiwen Ecosystem. All rights reserved.
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { KWVault } from "./storage/vault";
|
|
2
|
+
import { KWMigrationEngine } from "./models/migration";
|
|
3
|
+
import { KWAssetEngine } from "./storage/assets";
|
|
4
|
+
import { KWPipeline } from "./core/pipeline";
|
|
5
|
+
import { IKWStorage } from "./storage/db";
|
|
6
|
+
import { KWRecord } from "./models/record";
|
|
7
|
+
export declare class KaiwenApp {
|
|
8
|
+
private appId;
|
|
9
|
+
private storage;
|
|
10
|
+
vault: KWVault;
|
|
11
|
+
assetEngine: KWAssetEngine;
|
|
12
|
+
pipeline: KWPipeline;
|
|
13
|
+
migration: KWMigrationEngine;
|
|
14
|
+
constructor(appId: string, apiUrl: string, apiKey: string, storage?: IKWStorage);
|
|
15
|
+
login(mnemonic: string): Promise<void>;
|
|
16
|
+
saveRecord(userId: string, payload: any): Promise<KWRecord>;
|
|
17
|
+
getDecryptedRecord(id: string): Promise<any>;
|
|
18
|
+
pushSync(): Promise<number>;
|
|
19
|
+
pullSync(): Promise<number>;
|
|
20
|
+
}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KaiwenApp = void 0;
|
|
4
|
+
const vault_1 = require("./storage/vault");
|
|
5
|
+
const engine_1 = require("./crypto/engine");
|
|
6
|
+
const migration_1 = require("./models/migration");
|
|
7
|
+
const assets_1 = require("./storage/assets");
|
|
8
|
+
const pipeline_1 = require("./core/pipeline");
|
|
9
|
+
const db_1 = require("./storage/db");
|
|
10
|
+
const record_1 = require("./models/record");
|
|
11
|
+
const validator_1 = require("./utils/validator");
|
|
12
|
+
class KaiwenApp {
|
|
13
|
+
constructor(appId, apiUrl, apiKey, storage = new db_1.MemoryStorage()) {
|
|
14
|
+
this.appId = appId;
|
|
15
|
+
this.storage = storage;
|
|
16
|
+
this.vault = new vault_1.KWVault();
|
|
17
|
+
this.assetEngine = new assets_1.KWAssetEngine(this.vault);
|
|
18
|
+
this.pipeline = new pipeline_1.KWPipeline(this.vault, storage, apiUrl, apiKey);
|
|
19
|
+
this.migration = new migration_1.KWMigrationEngine(storage, 1);
|
|
20
|
+
}
|
|
21
|
+
async login(mnemonic) {
|
|
22
|
+
await this.vault.login(mnemonic, this.appId);
|
|
23
|
+
await this.migration.migrateAll();
|
|
24
|
+
}
|
|
25
|
+
async saveRecord(userId, payload) {
|
|
26
|
+
validator_1.KWValidator.validatePayload(payload);
|
|
27
|
+
const encrypted = await engine_1.KWCryptoEngine.encryptPayload(payload, this.vault.getKey());
|
|
28
|
+
const generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
29
|
+
const record = new record_1.KWRecord(generateId(), this.appId, userId, encrypted, Date.now() / 1000, 0, 1, 1);
|
|
30
|
+
await this.storage.saveRecord(record.id, record);
|
|
31
|
+
return record;
|
|
32
|
+
}
|
|
33
|
+
async getDecryptedRecord(id) {
|
|
34
|
+
const record = await this.storage.getRecord(id);
|
|
35
|
+
if (!record)
|
|
36
|
+
return null;
|
|
37
|
+
return await engine_1.KWCryptoEngine.decryptPayload(record.payload, this.vault.getKey());
|
|
38
|
+
}
|
|
39
|
+
async pushSync() {
|
|
40
|
+
return await this.pipeline.pushSync(this.appId);
|
|
41
|
+
}
|
|
42
|
+
async pullSync() {
|
|
43
|
+
return await this.pipeline.pullSync(this.appId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.KaiwenApp = KaiwenApp;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class KWException extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class VaultLockedException extends KWException {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class SyncFailedException extends KWException {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SyncFailedException = exports.VaultLockedException = exports.KWException = void 0;
|
|
4
|
+
class KWException extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "KWException";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.KWException = KWException;
|
|
11
|
+
class VaultLockedException extends KWException {
|
|
12
|
+
constructor() {
|
|
13
|
+
super("Vault is locked. User not authenticated.");
|
|
14
|
+
this.name = "VaultLockedException";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.VaultLockedException = VaultLockedException;
|
|
18
|
+
class SyncFailedException extends KWException {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(`Sync failed: ${message}`);
|
|
21
|
+
this.name = "SyncFailedException";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.SyncFailedException = SyncFailedException;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KWVault } from "../storage/vault";
|
|
2
|
+
import { IKWStorage } from "../storage/db";
|
|
3
|
+
export declare class KWPipeline {
|
|
4
|
+
private vault;
|
|
5
|
+
private storage;
|
|
6
|
+
private network;
|
|
7
|
+
constructor(vault: KWVault, storage: IKWStorage, apiUrl: string, apiKey: string);
|
|
8
|
+
pushSync(appId: string): Promise<number>;
|
|
9
|
+
pullSync(appId: string): Promise<number>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWPipeline = void 0;
|
|
4
|
+
const network_1 = require("../sync/network");
|
|
5
|
+
const record_1 = require("../models/record");
|
|
6
|
+
const logger_1 = require("../utils/logger");
|
|
7
|
+
class KWPipeline {
|
|
8
|
+
constructor(vault, storage, apiUrl, apiKey) {
|
|
9
|
+
this.vault = vault;
|
|
10
|
+
this.storage = storage;
|
|
11
|
+
this.network = new network_1.KWNetwork(apiUrl, apiKey);
|
|
12
|
+
}
|
|
13
|
+
async pushSync(appId) {
|
|
14
|
+
logger_1.KWLogger.info("Starting pushSync...");
|
|
15
|
+
const allRecords = await this.storage.getAllRecords();
|
|
16
|
+
const pending = allRecords.filter(r => r.is_synced === 0);
|
|
17
|
+
if (pending.length === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
const syncedCount = await this.network.push(pending);
|
|
20
|
+
for (const r of pending) {
|
|
21
|
+
r.is_synced = 1;
|
|
22
|
+
await this.storage.saveRecord(r.id, r);
|
|
23
|
+
}
|
|
24
|
+
return syncedCount;
|
|
25
|
+
}
|
|
26
|
+
async pullSync(appId) {
|
|
27
|
+
logger_1.KWLogger.info("Starting pullSync...");
|
|
28
|
+
const allRecords = await this.storage.getAllRecords();
|
|
29
|
+
let latestUpdate = 0;
|
|
30
|
+
allRecords.forEach(r => {
|
|
31
|
+
if (r.updated_at > latestUpdate)
|
|
32
|
+
latestUpdate = r.updated_at;
|
|
33
|
+
});
|
|
34
|
+
const incomingRecords = await this.network.pull(appId, latestUpdate);
|
|
35
|
+
let mergedCount = 0;
|
|
36
|
+
for (const remoteObj of incomingRecords) {
|
|
37
|
+
const remote = record_1.KWRecord.fromObject(remoteObj);
|
|
38
|
+
const local = await this.storage.getRecord(remote.id);
|
|
39
|
+
if (!local || remote.logical_clock > local.logical_clock) {
|
|
40
|
+
remote.is_synced = 1;
|
|
41
|
+
await this.storage.saveRecord(remote.id, remote);
|
|
42
|
+
mergedCount++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return mergedCount;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.KWPipeline = KWPipeline;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class KWCryptoEngine {
|
|
2
|
+
private static getSubtleCrypto;
|
|
3
|
+
private static bufferToBase64;
|
|
4
|
+
private static base64ToBuffer;
|
|
5
|
+
static deriveAppKeyFromMnemonic(mnemonic: string, appId: string): Promise<CryptoKey>;
|
|
6
|
+
static encryptPayload(payload: any, appKey: CryptoKey): Promise<string>;
|
|
7
|
+
static decryptPayload(encryptedPayloadBase64: string, appKey: CryptoKey): Promise<any>;
|
|
8
|
+
static encryptAssetChunk(data: Uint8Array, appKey: CryptoKey): Promise<string>;
|
|
9
|
+
static decryptAssetChunk(encryptedBase64: string, appKey: CryptoKey): Promise<Uint8Array>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWCryptoEngine = void 0;
|
|
4
|
+
class KWCryptoEngine {
|
|
5
|
+
static getSubtleCrypto() {
|
|
6
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
7
|
+
return crypto.subtle; // Modern Browsers & Deno
|
|
8
|
+
}
|
|
9
|
+
// Node.js fallback or RN polyfill
|
|
10
|
+
if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.subtle) {
|
|
11
|
+
return globalThis.crypto.subtle;
|
|
12
|
+
}
|
|
13
|
+
throw new Error("Web Crypto API (crypto.subtle) is not available in this environment.");
|
|
14
|
+
}
|
|
15
|
+
static bufferToBase64(buffer) {
|
|
16
|
+
let binary = '';
|
|
17
|
+
const len = buffer.byteLength;
|
|
18
|
+
for (let i = 0; i < len; i++) {
|
|
19
|
+
binary += String.fromCharCode(buffer[i]);
|
|
20
|
+
}
|
|
21
|
+
return btoa(binary);
|
|
22
|
+
}
|
|
23
|
+
static base64ToBuffer(base64) {
|
|
24
|
+
const binary = atob(base64);
|
|
25
|
+
const len = binary.length;
|
|
26
|
+
const bytes = new Uint8Array(len);
|
|
27
|
+
for (let i = 0; i < len; i++) {
|
|
28
|
+
bytes[i] = binary.charCodeAt(i);
|
|
29
|
+
}
|
|
30
|
+
return bytes;
|
|
31
|
+
}
|
|
32
|
+
static async deriveAppKeyFromMnemonic(mnemonic, appId) {
|
|
33
|
+
const subtle = this.getSubtleCrypto();
|
|
34
|
+
const encoder = new TextEncoder();
|
|
35
|
+
// 1. PBKDF2 Master Key Derivation (matches Python)
|
|
36
|
+
const pbkdf2KeyMaterial = await subtle.importKey("raw", encoder.encode(mnemonic), { name: "PBKDF2" }, false, ["deriveBits"]);
|
|
37
|
+
const pbkdf2Salt = encoder.encode("visuality_global_salt");
|
|
38
|
+
const masterKeyRaw = await subtle.deriveBits({ name: "PBKDF2", salt: pbkdf2Salt, iterations: 200000, hash: "SHA-256" }, pbkdf2KeyMaterial, 256);
|
|
39
|
+
// 2. HKDF App Key Derivation (matches Python)
|
|
40
|
+
const hkdfKeyMaterial = await subtle.importKey("raw", masterKeyRaw, { name: "HKDF" }, false, ["deriveKey"]);
|
|
41
|
+
return await subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(32), info: encoder.encode(appId) }, hkdfKeyMaterial, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
|
|
42
|
+
}
|
|
43
|
+
static async encryptPayload(payload, appKey) {
|
|
44
|
+
const encoder = new TextEncoder();
|
|
45
|
+
const data = encoder.encode(JSON.stringify(payload));
|
|
46
|
+
return this.encryptAssetChunk(data, appKey);
|
|
47
|
+
}
|
|
48
|
+
static async decryptPayload(encryptedPayloadBase64, appKey) {
|
|
49
|
+
const decoder = new TextDecoder();
|
|
50
|
+
const decryptedBuffer = await this.decryptAssetChunk(encryptedPayloadBase64, appKey);
|
|
51
|
+
return JSON.parse(decoder.decode(decryptedBuffer));
|
|
52
|
+
}
|
|
53
|
+
// Raw Uint8Array Encryption for Asset Engine
|
|
54
|
+
static async encryptAssetChunk(data, appKey) {
|
|
55
|
+
const subtle = this.getSubtleCrypto();
|
|
56
|
+
const iv = new Uint8Array(12);
|
|
57
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
58
|
+
crypto.getRandomValues(iv);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
for (let i = 0; i < 12; i++)
|
|
62
|
+
iv[i] = Math.floor(Math.random() * 256);
|
|
63
|
+
}
|
|
64
|
+
const encryptedBuffer = await subtle.encrypt({ name: "AES-GCM", iv: iv }, appKey, data);
|
|
65
|
+
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
66
|
+
const combined = new Uint8Array(iv.length + encryptedArray.length);
|
|
67
|
+
combined.set(iv, 0);
|
|
68
|
+
combined.set(encryptedArray, iv.length);
|
|
69
|
+
return this.bufferToBase64(combined);
|
|
70
|
+
}
|
|
71
|
+
static async decryptAssetChunk(encryptedBase64, appKey) {
|
|
72
|
+
const subtle = this.getSubtleCrypto();
|
|
73
|
+
const combined = this.base64ToBuffer(encryptedBase64);
|
|
74
|
+
const iv = combined.slice(0, 12);
|
|
75
|
+
const ciphertext = combined.slice(12);
|
|
76
|
+
const decryptedBuffer = await subtle.decrypt({ name: "AES-GCM", iv: iv }, appKey, ciphertext);
|
|
77
|
+
return new Uint8Array(decryptedBuffer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.KWCryptoEngine = KWCryptoEngine;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { KaiwenApp } from "./app";
|
|
2
|
+
export { IKWStorage, MemoryStorage } from "./storage/db";
|
|
3
|
+
export { KWRecord } from "./models/record";
|
|
4
|
+
export { KWVault } from "./storage/vault";
|
|
5
|
+
export { KWAssetEngine, KWAssetManifest } from "./storage/assets";
|
|
6
|
+
export { KWMigrationEngine } from "./models/migration";
|
|
7
|
+
export { KWCryptoEngine } from "./crypto/engine";
|
|
8
|
+
export { KWPipeline } from "./core/pipeline";
|
|
9
|
+
export { KWSyncStatus, KWRecordType } from "./models/enums";
|
|
10
|
+
export { KWException, VaultLockedException, SyncFailedException } from "./core/exceptions";
|
|
11
|
+
export { KWLogger } from "./utils/logger";
|
|
12
|
+
export { KWTracer } from "./utils/tracer";
|
|
13
|
+
export { KWValidator } from "./utils/validator";
|
|
14
|
+
export { KWNetwork } from "./sync/network";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWNetwork = exports.KWValidator = exports.KWTracer = exports.KWLogger = exports.SyncFailedException = exports.VaultLockedException = exports.KWException = exports.KWRecordType = exports.KWSyncStatus = exports.KWPipeline = exports.KWCryptoEngine = exports.KWMigrationEngine = exports.KWAssetEngine = exports.KWVault = exports.KWRecord = exports.MemoryStorage = exports.KaiwenApp = void 0;
|
|
4
|
+
var app_1 = require("./app");
|
|
5
|
+
Object.defineProperty(exports, "KaiwenApp", { enumerable: true, get: function () { return app_1.KaiwenApp; } });
|
|
6
|
+
var db_1 = require("./storage/db");
|
|
7
|
+
Object.defineProperty(exports, "MemoryStorage", { enumerable: true, get: function () { return db_1.MemoryStorage; } });
|
|
8
|
+
var record_1 = require("./models/record");
|
|
9
|
+
Object.defineProperty(exports, "KWRecord", { enumerable: true, get: function () { return record_1.KWRecord; } });
|
|
10
|
+
var vault_1 = require("./storage/vault");
|
|
11
|
+
Object.defineProperty(exports, "KWVault", { enumerable: true, get: function () { return vault_1.KWVault; } });
|
|
12
|
+
var assets_1 = require("./storage/assets");
|
|
13
|
+
Object.defineProperty(exports, "KWAssetEngine", { enumerable: true, get: function () { return assets_1.KWAssetEngine; } });
|
|
14
|
+
var migration_1 = require("./models/migration");
|
|
15
|
+
Object.defineProperty(exports, "KWMigrationEngine", { enumerable: true, get: function () { return migration_1.KWMigrationEngine; } });
|
|
16
|
+
var engine_1 = require("./crypto/engine");
|
|
17
|
+
Object.defineProperty(exports, "KWCryptoEngine", { enumerable: true, get: function () { return engine_1.KWCryptoEngine; } });
|
|
18
|
+
var pipeline_1 = require("./core/pipeline");
|
|
19
|
+
Object.defineProperty(exports, "KWPipeline", { enumerable: true, get: function () { return pipeline_1.KWPipeline; } });
|
|
20
|
+
var enums_1 = require("./models/enums");
|
|
21
|
+
Object.defineProperty(exports, "KWSyncStatus", { enumerable: true, get: function () { return enums_1.KWSyncStatus; } });
|
|
22
|
+
Object.defineProperty(exports, "KWRecordType", { enumerable: true, get: function () { return enums_1.KWRecordType; } });
|
|
23
|
+
var exceptions_1 = require("./core/exceptions");
|
|
24
|
+
Object.defineProperty(exports, "KWException", { enumerable: true, get: function () { return exceptions_1.KWException; } });
|
|
25
|
+
Object.defineProperty(exports, "VaultLockedException", { enumerable: true, get: function () { return exceptions_1.VaultLockedException; } });
|
|
26
|
+
Object.defineProperty(exports, "SyncFailedException", { enumerable: true, get: function () { return exceptions_1.SyncFailedException; } });
|
|
27
|
+
var logger_1 = require("./utils/logger");
|
|
28
|
+
Object.defineProperty(exports, "KWLogger", { enumerable: true, get: function () { return logger_1.KWLogger; } });
|
|
29
|
+
var tracer_1 = require("./utils/tracer");
|
|
30
|
+
Object.defineProperty(exports, "KWTracer", { enumerable: true, get: function () { return tracer_1.KWTracer; } });
|
|
31
|
+
var validator_1 = require("./utils/validator");
|
|
32
|
+
Object.defineProperty(exports, "KWValidator", { enumerable: true, get: function () { return validator_1.KWValidator; } });
|
|
33
|
+
var network_1 = require("./sync/network");
|
|
34
|
+
Object.defineProperty(exports, "KWNetwork", { enumerable: true, get: function () { return network_1.KWNetwork; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWRecordType = exports.KWSyncStatus = void 0;
|
|
4
|
+
var KWSyncStatus;
|
|
5
|
+
(function (KWSyncStatus) {
|
|
6
|
+
KWSyncStatus[KWSyncStatus["PENDING"] = 0] = "PENDING";
|
|
7
|
+
KWSyncStatus[KWSyncStatus["SYNCED"] = 1] = "SYNCED";
|
|
8
|
+
})(KWSyncStatus || (exports.KWSyncStatus = KWSyncStatus = {}));
|
|
9
|
+
var KWRecordType;
|
|
10
|
+
(function (KWRecordType) {
|
|
11
|
+
KWRecordType["TEXT"] = "text";
|
|
12
|
+
KWRecordType["ASSET"] = "asset";
|
|
13
|
+
})(KWRecordType || (exports.KWRecordType = KWRecordType = {}));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWMigrationEngine = void 0;
|
|
4
|
+
class KWMigrationEngine {
|
|
5
|
+
constructor(storage, currentSchemaVersion) {
|
|
6
|
+
this.storage = storage;
|
|
7
|
+
this.currentSchemaVersion = currentSchemaVersion;
|
|
8
|
+
}
|
|
9
|
+
async migrateAll() {
|
|
10
|
+
const records = await this.storage.getAllRecords();
|
|
11
|
+
for (const record of records) {
|
|
12
|
+
if (record.schema_version < this.currentSchemaVersion) {
|
|
13
|
+
record.schema_version = this.currentSchemaVersion;
|
|
14
|
+
record.updated_at = Date.now() / 1000;
|
|
15
|
+
record.logical_clock += 1;
|
|
16
|
+
record.is_synced = 0;
|
|
17
|
+
await this.storage.saveRecord(record.id, record);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.KWMigrationEngine = KWMigrationEngine;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class KWRecord {
|
|
2
|
+
id: string;
|
|
3
|
+
app_id: string;
|
|
4
|
+
user_id: string;
|
|
5
|
+
payload: string;
|
|
6
|
+
updated_at: number;
|
|
7
|
+
is_synced: number;
|
|
8
|
+
schema_version: number;
|
|
9
|
+
logical_clock: number;
|
|
10
|
+
constructor(id: string, app_id: string, user_id: string, payload: string, updated_at: number, is_synced?: number, schema_version?: number, logical_clock?: number);
|
|
11
|
+
static fromObject(obj: any): KWRecord;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWRecord = void 0;
|
|
4
|
+
class KWRecord {
|
|
5
|
+
constructor(id, app_id, user_id, payload, updated_at, is_synced = 0, schema_version = 1, logical_clock = 1) {
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.app_id = app_id;
|
|
8
|
+
this.user_id = user_id;
|
|
9
|
+
this.payload = payload;
|
|
10
|
+
this.updated_at = updated_at;
|
|
11
|
+
this.is_synced = is_synced;
|
|
12
|
+
this.schema_version = schema_version;
|
|
13
|
+
this.logical_clock = logical_clock;
|
|
14
|
+
}
|
|
15
|
+
static fromObject(obj) {
|
|
16
|
+
return new KWRecord(obj.id, obj.app_id, obj.user_id, obj.payload, obj.updated_at, obj.is_synced, obj.schema_version, obj.logical_clock);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.KWRecord = KWRecord;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWTracerContext = void 0;
|
|
4
|
+
class KWTracerContext {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.traceId = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.KWTracerContext = KWTracerContext;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { KWVault } from "./vault";
|
|
2
|
+
export interface KWAssetManifest {
|
|
3
|
+
assetId: string;
|
|
4
|
+
totalChunks: number;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
size: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class KWAssetEngine {
|
|
9
|
+
private vault;
|
|
10
|
+
private readonly CHUNK_SIZE;
|
|
11
|
+
constructor(vault: KWVault);
|
|
12
|
+
encryptAsset(fileBuffer: Uint8Array, mimeType: string): Promise<{
|
|
13
|
+
manifest: KWAssetManifest;
|
|
14
|
+
encryptedChunks: string[];
|
|
15
|
+
}>;
|
|
16
|
+
decryptAsset(encryptedChunks: string[], mimeType: string): Promise<Uint8Array>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWAssetEngine = void 0;
|
|
4
|
+
const engine_1 = require("../crypto/engine");
|
|
5
|
+
class KWAssetEngine {
|
|
6
|
+
constructor(vault) {
|
|
7
|
+
this.vault = vault;
|
|
8
|
+
this.CHUNK_SIZE = 1024 * 1024; // 1MB
|
|
9
|
+
}
|
|
10
|
+
async encryptAsset(fileBuffer, mimeType) {
|
|
11
|
+
if (!this.vault.isUnlocked())
|
|
12
|
+
throw new Error("Vault locked");
|
|
13
|
+
const key = this.vault.getKey();
|
|
14
|
+
const chunks = [];
|
|
15
|
+
const generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2);
|
|
16
|
+
const assetId = generateId();
|
|
17
|
+
for (let i = 0; i < fileBuffer.length; i += this.CHUNK_SIZE) {
|
|
18
|
+
const chunk = fileBuffer.slice(i, i + this.CHUNK_SIZE);
|
|
19
|
+
const encryptedChunk = await engine_1.KWCryptoEngine.encryptAssetChunk(chunk, key);
|
|
20
|
+
chunks.push(encryptedChunk);
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
manifest: {
|
|
24
|
+
assetId,
|
|
25
|
+
totalChunks: chunks.length,
|
|
26
|
+
mimeType,
|
|
27
|
+
size: fileBuffer.length
|
|
28
|
+
},
|
|
29
|
+
encryptedChunks: chunks
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async decryptAsset(encryptedChunks, mimeType) {
|
|
33
|
+
if (!this.vault.isUnlocked())
|
|
34
|
+
throw new Error("Vault locked");
|
|
35
|
+
const key = this.vault.getKey();
|
|
36
|
+
let totalLength = 0;
|
|
37
|
+
const decryptedChunks = [];
|
|
38
|
+
for (const chunk64 of encryptedChunks) {
|
|
39
|
+
const decrypted = await engine_1.KWCryptoEngine.decryptAssetChunk(chunk64, key);
|
|
40
|
+
decryptedChunks.push(decrypted);
|
|
41
|
+
totalLength += decrypted.length;
|
|
42
|
+
}
|
|
43
|
+
const result = new Uint8Array(totalLength);
|
|
44
|
+
let offset = 0;
|
|
45
|
+
for (const chunk of decryptedChunks) {
|
|
46
|
+
result.set(chunk, offset);
|
|
47
|
+
offset += chunk.length;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.KWAssetEngine = KWAssetEngine;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { KWRecord } from "../models/record";
|
|
2
|
+
export interface IKWStorage {
|
|
3
|
+
saveRecord(id: string, data: KWRecord): Promise<boolean>;
|
|
4
|
+
getRecord(id: string): Promise<KWRecord | null>;
|
|
5
|
+
getAllRecords(): Promise<KWRecord[]>;
|
|
6
|
+
}
|
|
7
|
+
export declare class MemoryStorage implements IKWStorage {
|
|
8
|
+
private store;
|
|
9
|
+
saveRecord(id: string, data: KWRecord): Promise<boolean>;
|
|
10
|
+
getRecord(id: string): Promise<KWRecord | null>;
|
|
11
|
+
getAllRecords(): Promise<KWRecord[]>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryStorage = void 0;
|
|
4
|
+
class MemoryStorage {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.store = new Map();
|
|
7
|
+
}
|
|
8
|
+
async saveRecord(id, data) {
|
|
9
|
+
this.store.set(id, data);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
async getRecord(id) {
|
|
13
|
+
return this.store.get(id) || null;
|
|
14
|
+
}
|
|
15
|
+
async getAllRecords() {
|
|
16
|
+
return Array.from(this.store.values());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.MemoryStorage = MemoryStorage;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWVault = void 0;
|
|
4
|
+
const engine_1 = require("../crypto/engine");
|
|
5
|
+
const exceptions_1 = require("../core/exceptions");
|
|
6
|
+
class KWVault {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.appKey = null;
|
|
9
|
+
}
|
|
10
|
+
async login(mnemonic, appId) {
|
|
11
|
+
this.appKey = await engine_1.KWCryptoEngine.deriveAppKeyFromMnemonic(mnemonic, appId);
|
|
12
|
+
}
|
|
13
|
+
isUnlocked() {
|
|
14
|
+
return this.appKey !== null;
|
|
15
|
+
}
|
|
16
|
+
getKey() {
|
|
17
|
+
if (!this.appKey)
|
|
18
|
+
throw new exceptions_1.VaultLockedException();
|
|
19
|
+
return this.appKey;
|
|
20
|
+
}
|
|
21
|
+
lock() {
|
|
22
|
+
this.appKey = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.KWVault = KWVault;
|