@telestack/storage 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 +132 -0
- package/debug-fetch.js +28 -0
- package/dist/index.d.mts +329 -0
- package/dist/index.d.ts +329 -0
- package/dist/index.global.js +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +37 -0
- package/src/BatchBuilder.ts +48 -0
- package/src/CryptoHelper.ts +72 -0
- package/src/HttpClient.ts +152 -0
- package/src/ImageHelper.ts +80 -0
- package/src/QueryBuilder.ts +45 -0
- package/src/StorageRef.ts +153 -0
- package/src/TelestackStorage.ts +93 -0
- package/src/UploadTask.ts +332 -0
- package/src/index.ts +28 -0
- package/src/types.ts +91 -0
- package/test-e2e.js +142 -0
- package/test-e2e.ts +182 -0
- package/test-output.txt +0 -0
- package/tsconfig.json +17 -0
package/test-e2e.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
const dom = new JSDOM();
|
|
3
|
+
global.XMLHttpRequest = dom.window.XMLHttpRequest as any;
|
|
4
|
+
global.File = dom.window.File as any;
|
|
5
|
+
global.Blob = dom.window.Blob as any;
|
|
6
|
+
|
|
7
|
+
import { SignJWT } from 'jose';
|
|
8
|
+
import { TelestackStorage } from './dist/index.js';
|
|
9
|
+
|
|
10
|
+
const TENANT_ID = 'test-tenant';
|
|
11
|
+
const USER_ID = 'user-sdk-test';
|
|
12
|
+
const JWT_SECRET = new TextEncoder().encode('telestack-super-secret-key-1234567890');
|
|
13
|
+
|
|
14
|
+
async function getAuthToken() {
|
|
15
|
+
return await new SignJWT({
|
|
16
|
+
sub: USER_ID,
|
|
17
|
+
tenant_id: TENANT_ID,
|
|
18
|
+
role: 'admin',
|
|
19
|
+
email: 'test@example.com'
|
|
20
|
+
})
|
|
21
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
22
|
+
.setIssuedAt()
|
|
23
|
+
.setIssuer('telestack-auth')
|
|
24
|
+
.setAudience('telestack-storage')
|
|
25
|
+
.setExpirationTime('2h')
|
|
26
|
+
.sign(JWT_SECRET);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function runSdkTests() {
|
|
30
|
+
const token = await getAuthToken();
|
|
31
|
+
|
|
32
|
+
const storage = new TelestackStorage({
|
|
33
|
+
baseUrl: 'http://localhost:8787',
|
|
34
|
+
tenantId: TENANT_ID,
|
|
35
|
+
token: token,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
console.log('🧪 Starting Telestack Storage Web SDK Comprehensive Tests...\n');
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// --- 1. Simple Upload ---
|
|
42
|
+
console.log('1️⃣ Testing Simple Upload (UploadTask)...');
|
|
43
|
+
const simpleContent = new TextEncoder().encode('Hello Telestack SDK Simple Test!');
|
|
44
|
+
const simpleBlob = new Blob([simpleContent], { type: 'text/plain' });
|
|
45
|
+
|
|
46
|
+
const simpleTask = storage.ref('sdk-tests/simple.txt').put(simpleBlob, { userId: USER_ID });
|
|
47
|
+
|
|
48
|
+
await new Promise<void>((resolve, reject) => {
|
|
49
|
+
simpleTask.on('state_changed',
|
|
50
|
+
(snap: any) => console.log(` Progress: ${(snap.bytesTransferred / snap.totalBytes * 100).toFixed(0)}%`),
|
|
51
|
+
(err: any) => reject(err),
|
|
52
|
+
() => resolve()
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
console.log('✅ Simple Upload Completed\n');
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// --- 2. Querying Metadata ---
|
|
59
|
+
console.log('2️⃣ Testing Query Builder...');
|
|
60
|
+
const queryResults = await storage.query()
|
|
61
|
+
.nameContains('simple.txt')
|
|
62
|
+
.limit(5)
|
|
63
|
+
.get();
|
|
64
|
+
console.log(` Found ${queryResults.length} files matching query.`);
|
|
65
|
+
if (queryResults.length === 0) throw new Error('Query failed to find uploaded file.');
|
|
66
|
+
console.log('✅ Query Builder Passed\n');
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
// --- 3. Directory Listing ---
|
|
70
|
+
console.log('3️⃣ Testing Directory Refs (listAll)...');
|
|
71
|
+
const dirFiles = await storage.dir('sdk-tests/').listAll();
|
|
72
|
+
console.log(` Found ${dirFiles.length} files in directory.`);
|
|
73
|
+
console.log('✅ Directory Listing Passed\n');
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// --- 4. Resumable Upload (Chunking) ---
|
|
77
|
+
console.log('4️⃣ Testing Resumable Upload (Chunks + Pause/Resume)...');
|
|
78
|
+
// Generate a 60MB string dynamically for testing multipart chunking
|
|
79
|
+
const bigContentSize = 60 * 1024 * 1024;
|
|
80
|
+
const bigArray = new Uint8Array(bigContentSize);
|
|
81
|
+
bigArray.fill(65); // Fill with 'A'
|
|
82
|
+
const bigBlob = new Blob([bigArray], { type: 'application/zip' });
|
|
83
|
+
|
|
84
|
+
const resumableTask = storage.ref('sdk-tests/big-resumable.bin').put(bigBlob, {
|
|
85
|
+
userId: USER_ID,
|
|
86
|
+
chunkSize: 20 * 1024 * 1024, // 20MB chunks
|
|
87
|
+
metadata: { test: 'sdk-resumable' }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await new Promise<void>((resolve, reject) => {
|
|
91
|
+
let paused = false;
|
|
92
|
+
resumableTask.on('state_changed',
|
|
93
|
+
(snap: any) => {
|
|
94
|
+
console.log(` Resumable Progress: ${(snap.bytesTransferred / snap.totalBytes * 100).toFixed(0)}% (${snap.bytesTransferred} bytes)`);
|
|
95
|
+
// Pause halfway
|
|
96
|
+
if (snap.bytesTransferred > 20 * 1024 * 1024 && !paused) {
|
|
97
|
+
paused = true;
|
|
98
|
+
console.log(' ⏸️ Pausing upload...');
|
|
99
|
+
resumableTask.pause();
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
console.log(' ▶️ Resuming upload...');
|
|
102
|
+
resumableTask.resume();
|
|
103
|
+
}, 2000);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
(err: any) => reject(err),
|
|
107
|
+
() => resolve()
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
console.log('✅ Resumable Multipart Upload Passed\n');
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
// --- 5. Download URL & Metadata CRUD ---
|
|
114
|
+
console.log('5️⃣ Testing Download URL & Metadata Update...');
|
|
115
|
+
const simpleRef = storage.ref('sdk-tests/simple.txt');
|
|
116
|
+
const url = await simpleRef.getDownloadUrl();
|
|
117
|
+
console.log(' Download URL:', url.substring(0, 50) + '...');
|
|
118
|
+
|
|
119
|
+
await simpleRef.updateMetadata({ newField: 'UpdatedSDKMetadata' });
|
|
120
|
+
const updatedMeta = await simpleRef.getMetadata();
|
|
121
|
+
if (updatedMeta.metadata.newField !== 'UpdatedSDKMetadata') throw new Error('Metadata update failed');
|
|
122
|
+
console.log('✅ Metadata Update Passed\n');
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
// --- 5.5 End-to-End Encryption (E2EE) ---
|
|
126
|
+
console.log('🔒 Testing End-to-End Encryption (E2EE)...');
|
|
127
|
+
const { CryptoHelper } = require('./dist/index.js');
|
|
128
|
+
const e2eKey = await CryptoHelper.generateKey();
|
|
129
|
+
|
|
130
|
+
const secretContent = new TextEncoder().encode('Super Secret Confidential Data!');
|
|
131
|
+
const secretBlob = new Blob([secretContent], { type: 'text/plain' });
|
|
132
|
+
|
|
133
|
+
const secureTask = storage.ref('sdk-tests/secure-data.txt').put(secretBlob, {
|
|
134
|
+
userId: USER_ID,
|
|
135
|
+
encryptionKey: e2eKey
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await new Promise<void>((resolve, reject) => {
|
|
139
|
+
secureTask.on('state_changed',
|
|
140
|
+
() => { },
|
|
141
|
+
(err: any) => reject(err),
|
|
142
|
+
() => resolve()
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Verify we can fetch and decrypt it successfully
|
|
147
|
+
console.log(' Downloading and decrypting E2EE blob...');
|
|
148
|
+
const decryptedBlob = await storage.ref('sdk-tests/secure-data.txt').getDecryptedBlob(e2eKey);
|
|
149
|
+
const decryptedText = await decryptedBlob.text();
|
|
150
|
+
|
|
151
|
+
if (decryptedText !== 'Super Secret Confidential Data!') {
|
|
152
|
+
throw new Error('E2EE Decryption failed or ciphertext mismatch');
|
|
153
|
+
}
|
|
154
|
+
console.log('✅ End-to-End Encryption (E2EE) Passed\n');
|
|
155
|
+
|
|
156
|
+
// --- 6. Batch Operations ---
|
|
157
|
+
console.log('6️⃣ Testing Batch/Mass Operations over network...');
|
|
158
|
+
console.log(' Moving files to /archive...');
|
|
159
|
+
const moveRes = await storage.batch()
|
|
160
|
+
.move(['sdk-tests/simple.txt', 'sdk-tests/big-resumable.bin'], 'sdk-archive/tests/')
|
|
161
|
+
.run();
|
|
162
|
+
|
|
163
|
+
if (!moveRes.success) throw new Error('Batch Move failed');
|
|
164
|
+
console.log('✅ Batch Move Passed\n');
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
// --- Final Cleanup ---
|
|
168
|
+
console.log('🧹 Formatting cleanup (Batch Delete)...');
|
|
169
|
+
const finalDel = await storage.batch()
|
|
170
|
+
.delete(['sdk-archive/tests/simple.txt', 'sdk-archive/tests/big-resumable.bin', 'sdk-tests/secure-data.txt'])
|
|
171
|
+
.run() as any;
|
|
172
|
+
console.log(` Deleted ${finalDel.deletedCount} files.`);
|
|
173
|
+
|
|
174
|
+
console.log('\n🎉 ALL WEB SDK TESTS PASSED SUCCESSFULLY! The SDK is robust.');
|
|
175
|
+
|
|
176
|
+
} catch (err: any) {
|
|
177
|
+
console.error('\n❌ WEB SDK TEST FAILED:', err.message || err);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
runSdkTests();
|
package/test-output.txt
ADDED
|
Binary file
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"isolatedModules": true
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"src/**/*"
|
|
16
|
+
]
|
|
17
|
+
}
|