@ruvector/edge-net 0.4.6 → 0.5.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/package.json +13 -2
- package/plugins/cli.js +395 -0
- package/plugins/implementations/compression.js +132 -0
- package/plugins/implementations/e2e-encryption.js +160 -0
- package/plugins/implementations/federated-learning.js +249 -0
- package/plugins/implementations/reputation-staking.js +243 -0
- package/plugins/implementations/swarm-intelligence.js +386 -0
- package/plugins/index.js +90 -0
- package/plugins/plugin-loader.js +440 -0
- package/plugins/plugin-manifest.js +702 -0
- package/plugins/plugin-sdk.js +496 -0
- package/tests/plugin-system-test.js +382 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruvector/edge-net",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Distributed compute intelligence network with WASM cryptographic security - contribute browser compute, spawn distributed AI agents, earn credits. Features Ed25519 signing, PiKey identity, Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, ONNX inference, WebRTC signaling, CRDT ledger, and multi-agent workflows.",
|
|
6
6
|
"main": "ruvector_edge_net.js",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"ruvector-edge": "./cli.js",
|
|
12
12
|
"edge-net-join": "./join.js",
|
|
13
13
|
"edge-net-genesis": "./genesis.js",
|
|
14
|
-
"edge-net-firebase-setup": "./firebase-setup.js"
|
|
14
|
+
"edge-net-firebase-setup": "./firebase-setup.js",
|
|
15
|
+
"edge-net-plugins": "./plugins/cli.js"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
18
|
"wasm",
|
|
@@ -105,6 +106,7 @@
|
|
|
105
106
|
"secure-access.js",
|
|
106
107
|
"credits.js",
|
|
107
108
|
"task-execution-handler.js",
|
|
109
|
+
"plugins/",
|
|
108
110
|
"README.md",
|
|
109
111
|
"LICENSE"
|
|
110
112
|
],
|
|
@@ -178,6 +180,15 @@
|
|
|
178
180
|
},
|
|
179
181
|
"./task-execution": {
|
|
180
182
|
"import": "./task-execution-handler.js"
|
|
183
|
+
},
|
|
184
|
+
"./plugins": {
|
|
185
|
+
"import": "./plugins/index.js"
|
|
186
|
+
},
|
|
187
|
+
"./plugins/sdk": {
|
|
188
|
+
"import": "./plugins/plugin-sdk.js"
|
|
189
|
+
},
|
|
190
|
+
"./plugins/catalog": {
|
|
191
|
+
"import": "./plugins/plugin-manifest.js"
|
|
181
192
|
}
|
|
182
193
|
},
|
|
183
194
|
"sideEffects": [
|
package/plugins/cli.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Edge-Net Plugin CLI
|
|
4
|
+
*
|
|
5
|
+
* Manage plugins from the command line.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* edge-net plugins list List available plugins
|
|
9
|
+
* edge-net plugins info <id> Show plugin details
|
|
10
|
+
* edge-net plugins enable <id> Enable a plugin
|
|
11
|
+
* edge-net plugins disable <id> Disable a plugin
|
|
12
|
+
* edge-net plugins bundles List plugin bundles
|
|
13
|
+
* edge-net plugins create <name> Create new plugin from template
|
|
14
|
+
* edge-net plugins validate <path> Validate a custom plugin
|
|
15
|
+
*
|
|
16
|
+
* @module @ruvector/edge-net/plugins/cli
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
20
|
+
import { join, dirname } from 'path';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
|
+
import {
|
|
23
|
+
PLUGIN_CATALOG,
|
|
24
|
+
PLUGIN_BUNDLES,
|
|
25
|
+
PluginCategory,
|
|
26
|
+
PluginTier,
|
|
27
|
+
} from './plugin-manifest.js';
|
|
28
|
+
import { PluginManager } from './plugin-loader.js';
|
|
29
|
+
import { generatePluginTemplate, validateManifest, getRegistry } from './plugin-sdk.js';
|
|
30
|
+
|
|
31
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
|
|
33
|
+
// ============================================
|
|
34
|
+
// CLI COMMANDS
|
|
35
|
+
// ============================================
|
|
36
|
+
|
|
37
|
+
const commands = {
|
|
38
|
+
/**
|
|
39
|
+
* List all available plugins
|
|
40
|
+
*/
|
|
41
|
+
list: async (args) => {
|
|
42
|
+
const filter = args[0]; // Optional: category filter
|
|
43
|
+
|
|
44
|
+
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
|
45
|
+
console.log('║ EDGE-NET PLUGIN CATALOG ║');
|
|
46
|
+
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
|
47
|
+
|
|
48
|
+
const plugins = Object.values(PLUGIN_CATALOG);
|
|
49
|
+
const grouped = {};
|
|
50
|
+
|
|
51
|
+
for (const plugin of plugins) {
|
|
52
|
+
if (filter && plugin.category !== filter && plugin.tier !== filter) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!grouped[plugin.category]) {
|
|
56
|
+
grouped[plugin.category] = [];
|
|
57
|
+
}
|
|
58
|
+
grouped[plugin.category].push(plugin);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const [category, categoryPlugins] of Object.entries(grouped)) {
|
|
62
|
+
console.log(`\n┌─ ${category.toUpperCase()} ─────────────────────────────────────────┐`);
|
|
63
|
+
|
|
64
|
+
for (const plugin of categoryPlugins) {
|
|
65
|
+
const tier = getTierBadge(plugin.tier);
|
|
66
|
+
const status = getStatusIcon(plugin);
|
|
67
|
+
console.log(`│ ${status} ${plugin.id.padEnd(30)} ${tier}`);
|
|
68
|
+
console.log(`│ ${plugin.description.slice(0, 55)}...`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log('└────────────────────────────────────────────────────────┘');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`\n📦 Total: ${plugins.length} plugins in ${Object.keys(grouped).length} categories\n`);
|
|
75
|
+
console.log('Use: edge-net plugins info <id> for details');
|
|
76
|
+
console.log('Use: edge-net plugins enable <id> to enable a plugin\n');
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Show plugin details
|
|
81
|
+
*/
|
|
82
|
+
info: async (args) => {
|
|
83
|
+
const pluginId = args[0];
|
|
84
|
+
if (!pluginId) {
|
|
85
|
+
console.error('Usage: edge-net plugins info <plugin-id>');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const plugin = PLUGIN_CATALOG[pluginId];
|
|
90
|
+
if (!plugin) {
|
|
91
|
+
console.error(`Plugin not found: ${pluginId}`);
|
|
92
|
+
console.log('\nAvailable plugins:');
|
|
93
|
+
Object.keys(PLUGIN_CATALOG).forEach(id => console.log(` - ${id}`));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
|
98
|
+
console.log(`║ ${plugin.name.padEnd(60)}║`);
|
|
99
|
+
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
|
100
|
+
|
|
101
|
+
console.log(` ID: ${plugin.id}`);
|
|
102
|
+
console.log(` Version: ${plugin.version}`);
|
|
103
|
+
console.log(` Category: ${plugin.category}`);
|
|
104
|
+
console.log(` Tier: ${getTierBadge(plugin.tier)}`);
|
|
105
|
+
console.log(` Description: ${plugin.description}`);
|
|
106
|
+
|
|
107
|
+
if (plugin.capabilities?.length > 0) {
|
|
108
|
+
console.log(`\n Capabilities Required:`);
|
|
109
|
+
plugin.capabilities.forEach(cap => console.log(` • ${cap}`));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (plugin.dependencies?.length > 0) {
|
|
113
|
+
console.log(`\n Dependencies:`);
|
|
114
|
+
plugin.dependencies.forEach(dep => console.log(` → ${dep}`));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (plugin.tags?.length > 0) {
|
|
118
|
+
console.log(`\n Tags: ${plugin.tags.join(', ')}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (plugin.configSchema?.properties) {
|
|
122
|
+
console.log('\n Configuration Options:');
|
|
123
|
+
for (const [key, prop] of Object.entries(plugin.configSchema.properties)) {
|
|
124
|
+
const def = prop.default !== undefined ? ` (default: ${JSON.stringify(prop.default)})` : '';
|
|
125
|
+
console.log(` ${key}: ${prop.type}${def}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('\n Usage:');
|
|
130
|
+
console.log(` const plugin = await plugins.load('${pluginId}');`);
|
|
131
|
+
console.log('');
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* List plugin bundles
|
|
136
|
+
*/
|
|
137
|
+
bundles: async () => {
|
|
138
|
+
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
|
139
|
+
console.log('║ PLUGIN BUNDLES ║');
|
|
140
|
+
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
|
141
|
+
|
|
142
|
+
for (const [id, bundle] of Object.entries(PLUGIN_BUNDLES)) {
|
|
143
|
+
console.log(`┌─ ${bundle.name.toUpperCase()} ─────────────────────────────────────────┐`);
|
|
144
|
+
console.log(`│ ID: ${id}`);
|
|
145
|
+
console.log(`│ ${bundle.description}`);
|
|
146
|
+
console.log('│');
|
|
147
|
+
console.log('│ Plugins:');
|
|
148
|
+
if (bundle.plugins.length === 0) {
|
|
149
|
+
console.log('│ (none - minimal bundle)');
|
|
150
|
+
} else {
|
|
151
|
+
bundle.plugins.forEach(p => console.log(`│ • ${p}`));
|
|
152
|
+
}
|
|
153
|
+
console.log('└────────────────────────────────────────────────────────────┘\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('Usage: await plugins.loadBundle("bundle-name")\n');
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create new plugin from template
|
|
161
|
+
*/
|
|
162
|
+
create: async (args) => {
|
|
163
|
+
const name = args[0];
|
|
164
|
+
if (!name) {
|
|
165
|
+
console.error('Usage: edge-net plugins create <plugin-name>');
|
|
166
|
+
console.log('\nOptions:');
|
|
167
|
+
console.log(' --category <category> Plugin category (default: core)');
|
|
168
|
+
console.log(' --tier <tier> Plugin tier (default: experimental)');
|
|
169
|
+
console.log(' --output <dir> Output directory (default: ./plugins)');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Parse options
|
|
174
|
+
const category = getArg(args, '--category') || 'core';
|
|
175
|
+
const tier = getArg(args, '--tier') || 'experimental';
|
|
176
|
+
const outputDir = getArg(args, '--output') || './plugins';
|
|
177
|
+
|
|
178
|
+
const id = `custom.${name.toLowerCase().replace(/\s+/g, '-')}`;
|
|
179
|
+
const pascalName = name.split(/[-\s]+/).map(s =>
|
|
180
|
+
s.charAt(0).toUpperCase() + s.slice(1)
|
|
181
|
+
).join('');
|
|
182
|
+
|
|
183
|
+
console.log(`\n🔧 Creating plugin: ${name}\n`);
|
|
184
|
+
|
|
185
|
+
const template = generatePluginTemplate({
|
|
186
|
+
id,
|
|
187
|
+
name,
|
|
188
|
+
description: `Custom edge-net plugin: ${name}`,
|
|
189
|
+
category: PluginCategory[category.toUpperCase()] || PluginCategory.CORE,
|
|
190
|
+
tier: PluginTier[tier.toUpperCase()] || PluginTier.EXPERIMENTAL,
|
|
191
|
+
capabilities: [],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Create output directory
|
|
195
|
+
const pluginDir = join(outputDir, name.toLowerCase().replace(/\s+/g, '-'));
|
|
196
|
+
if (!existsSync(pluginDir)) {
|
|
197
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Write plugin file
|
|
201
|
+
const pluginFile = join(pluginDir, 'index.js');
|
|
202
|
+
writeFileSync(pluginFile, template);
|
|
203
|
+
|
|
204
|
+
// Write package.json
|
|
205
|
+
const packageJson = {
|
|
206
|
+
name: `@edge-net-plugin/${name.toLowerCase().replace(/\s+/g, '-')}`,
|
|
207
|
+
version: '1.0.0',
|
|
208
|
+
type: 'module',
|
|
209
|
+
main: 'index.js',
|
|
210
|
+
description: `Custom edge-net plugin: ${name}`,
|
|
211
|
+
keywords: ['edge-net', 'plugin', category],
|
|
212
|
+
peerDependencies: {
|
|
213
|
+
'@ruvector/edge-net': '^0.4.0',
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
writeFileSync(join(pluginDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
217
|
+
|
|
218
|
+
// Write README
|
|
219
|
+
const readme = `# ${name}
|
|
220
|
+
|
|
221
|
+
Custom edge-net plugin.
|
|
222
|
+
|
|
223
|
+
## Installation
|
|
224
|
+
|
|
225
|
+
\`\`\`bash
|
|
226
|
+
npm install @edge-net-plugin/${name.toLowerCase().replace(/\s+/g, '-')}
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
## Usage
|
|
230
|
+
|
|
231
|
+
\`\`\`javascript
|
|
232
|
+
import { getRegistry } from '@ruvector/edge-net/plugins/sdk';
|
|
233
|
+
import { ${pascalName}Plugin } from './${name.toLowerCase().replace(/\s+/g, '-')}';
|
|
234
|
+
|
|
235
|
+
// Register plugin
|
|
236
|
+
getRegistry().register(${pascalName}Plugin);
|
|
237
|
+
|
|
238
|
+
// Use with plugin manager
|
|
239
|
+
const plugins = PluginManager.getInstance();
|
|
240
|
+
const myPlugin = await plugins.load('${id}');
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
## Configuration
|
|
244
|
+
|
|
245
|
+
See \`configSchema\` in the plugin manifest for available options.
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|
|
250
|
+
`;
|
|
251
|
+
writeFileSync(join(pluginDir, 'README.md'), readme);
|
|
252
|
+
|
|
253
|
+
console.log(`✅ Created plugin at: ${pluginDir}`);
|
|
254
|
+
console.log('');
|
|
255
|
+
console.log('Files created:');
|
|
256
|
+
console.log(` 📄 ${pluginFile}`);
|
|
257
|
+
console.log(` 📄 ${join(pluginDir, 'package.json')}`);
|
|
258
|
+
console.log(` 📄 ${join(pluginDir, 'README.md')}`);
|
|
259
|
+
console.log('');
|
|
260
|
+
console.log('Next steps:');
|
|
261
|
+
console.log(` 1. Edit ${pluginFile} to add your logic`);
|
|
262
|
+
console.log(' 2. Register with: getRegistry().register(YourPlugin)');
|
|
263
|
+
console.log(' 3. Load with: plugins.load("' + id + '")');
|
|
264
|
+
console.log('');
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Validate a plugin
|
|
269
|
+
*/
|
|
270
|
+
validate: async (args) => {
|
|
271
|
+
const pluginPath = args[0];
|
|
272
|
+
if (!pluginPath) {
|
|
273
|
+
console.error('Usage: edge-net plugins validate <plugin-path>');
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(`\n🔍 Validating plugin: ${pluginPath}\n`);
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const plugin = await import(pluginPath);
|
|
281
|
+
const PluginClass = plugin.default || Object.values(plugin)[0];
|
|
282
|
+
|
|
283
|
+
if (!PluginClass) {
|
|
284
|
+
console.error('❌ No plugin class found in module');
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Validate manifest
|
|
289
|
+
if (!PluginClass.manifest) {
|
|
290
|
+
console.error('❌ Plugin missing static manifest property');
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const validation = validateManifest(PluginClass.manifest);
|
|
295
|
+
|
|
296
|
+
if (validation.valid) {
|
|
297
|
+
console.log('✅ Plugin is valid!\n');
|
|
298
|
+
console.log('Manifest:');
|
|
299
|
+
console.log(JSON.stringify(PluginClass.manifest, null, 2));
|
|
300
|
+
} else {
|
|
301
|
+
console.log('❌ Plugin validation failed:\n');
|
|
302
|
+
validation.errors.forEach(err => console.log(` • ${err}`));
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(`❌ Error loading plugin: ${error.message}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Show help
|
|
313
|
+
*/
|
|
314
|
+
help: async () => {
|
|
315
|
+
console.log(`
|
|
316
|
+
Edge-Net Plugin Management
|
|
317
|
+
|
|
318
|
+
Usage: edge-net plugins <command> [options]
|
|
319
|
+
|
|
320
|
+
Commands:
|
|
321
|
+
list [category] List available plugins (filter by category/tier)
|
|
322
|
+
info <id> Show detailed plugin information
|
|
323
|
+
bundles List plugin bundles
|
|
324
|
+
create <name> Create new plugin from template
|
|
325
|
+
validate <path> Validate a custom plugin file
|
|
326
|
+
|
|
327
|
+
Categories:
|
|
328
|
+
core, network, crypto, privacy, ai, economic, storage, exotic
|
|
329
|
+
|
|
330
|
+
Tiers:
|
|
331
|
+
stable, beta, experimental, research
|
|
332
|
+
|
|
333
|
+
Examples:
|
|
334
|
+
edge-net plugins list
|
|
335
|
+
edge-net plugins list privacy
|
|
336
|
+
edge-net plugins info crypto.zk-proofs
|
|
337
|
+
edge-net plugins bundles
|
|
338
|
+
edge-net plugins create my-plugin --category ai
|
|
339
|
+
edge-net plugins validate ./my-plugin/index.js
|
|
340
|
+
`);
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// ============================================
|
|
345
|
+
// HELPERS
|
|
346
|
+
// ============================================
|
|
347
|
+
|
|
348
|
+
function getTierBadge(tier) {
|
|
349
|
+
const badges = {
|
|
350
|
+
stable: '🟢 STABLE',
|
|
351
|
+
beta: '🟡 BETA',
|
|
352
|
+
experimental: '🟠 EXPERIMENTAL',
|
|
353
|
+
research: '🔴 RESEARCH',
|
|
354
|
+
};
|
|
355
|
+
return badges[tier] || tier;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function getStatusIcon(plugin) {
|
|
359
|
+
// In real implementation, check if enabled
|
|
360
|
+
return '○';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function getArg(args, flag) {
|
|
364
|
+
const idx = args.indexOf(flag);
|
|
365
|
+
if (idx >= 0 && args[idx + 1]) {
|
|
366
|
+
return args[idx + 1];
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ============================================
|
|
372
|
+
// MAIN
|
|
373
|
+
// ============================================
|
|
374
|
+
|
|
375
|
+
async function main() {
|
|
376
|
+
const args = process.argv.slice(2);
|
|
377
|
+
const command = args[0] || 'help';
|
|
378
|
+
const commandArgs = args.slice(1);
|
|
379
|
+
|
|
380
|
+
if (commands[command]) {
|
|
381
|
+
await commands[command](commandArgs);
|
|
382
|
+
} else {
|
|
383
|
+
console.error(`Unknown command: ${command}`);
|
|
384
|
+
await commands.help();
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Run if executed directly
|
|
390
|
+
if (process.argv[1]?.includes('cli.js') || process.argv[1]?.includes('plugins')) {
|
|
391
|
+
main().catch(console.error);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export { commands };
|
|
395
|
+
export default main;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Compression Plugin
|
|
3
|
+
*
|
|
4
|
+
* LZ4/Zstd compression for network payloads and storage.
|
|
5
|
+
* Uses WASM for high-performance compression.
|
|
6
|
+
*
|
|
7
|
+
* @module @ruvector/edge-net/plugins/compression
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Simple LZ4-like compression (for demo - use actual WASM in production)
|
|
11
|
+
export class CompressionPlugin {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
this.config = {
|
|
14
|
+
algorithm: config.algorithm || 'lz4',
|
|
15
|
+
level: config.level || 3,
|
|
16
|
+
threshold: config.threshold || 1024,
|
|
17
|
+
};
|
|
18
|
+
this.stats = {
|
|
19
|
+
compressed: 0,
|
|
20
|
+
decompressed: 0,
|
|
21
|
+
bytesIn: 0,
|
|
22
|
+
bytesOut: 0,
|
|
23
|
+
ratio: 0,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compress data if above threshold
|
|
29
|
+
*/
|
|
30
|
+
compress(data) {
|
|
31
|
+
const input = typeof data === 'string' ? Buffer.from(data) : data;
|
|
32
|
+
|
|
33
|
+
if (input.length < this.config.threshold) {
|
|
34
|
+
return { compressed: false, data: input };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Simple RLE-based compression (demo)
|
|
38
|
+
const compressed = this._rleCompress(input);
|
|
39
|
+
|
|
40
|
+
this.stats.compressed++;
|
|
41
|
+
this.stats.bytesIn += input.length;
|
|
42
|
+
this.stats.bytesOut += compressed.length;
|
|
43
|
+
this.stats.ratio = this.stats.bytesOut / this.stats.bytesIn;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
compressed: true,
|
|
47
|
+
data: compressed,
|
|
48
|
+
originalSize: input.length,
|
|
49
|
+
compressedSize: compressed.length,
|
|
50
|
+
ratio: compressed.length / input.length,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Decompress data
|
|
56
|
+
*/
|
|
57
|
+
decompress(data, wasCompressed = true) {
|
|
58
|
+
if (!wasCompressed) {
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const decompressed = this._rleDecompress(data);
|
|
63
|
+
this.stats.decompressed++;
|
|
64
|
+
|
|
65
|
+
return decompressed;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Simple RLE compression (for demo)
|
|
70
|
+
*/
|
|
71
|
+
_rleCompress(input) {
|
|
72
|
+
const output = [];
|
|
73
|
+
let i = 0;
|
|
74
|
+
|
|
75
|
+
while (i < input.length) {
|
|
76
|
+
const byte = input[i];
|
|
77
|
+
let count = 1;
|
|
78
|
+
|
|
79
|
+
while (i + count < input.length &&
|
|
80
|
+
input[i + count] === byte &&
|
|
81
|
+
count < 255) {
|
|
82
|
+
count++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (count >= 4) {
|
|
86
|
+
output.push(0xFF, count, byte);
|
|
87
|
+
} else {
|
|
88
|
+
for (let j = 0; j < count; j++) {
|
|
89
|
+
if (byte === 0xFF) {
|
|
90
|
+
output.push(0xFF, 1, byte);
|
|
91
|
+
} else {
|
|
92
|
+
output.push(byte);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
i += count;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return Buffer.from(output);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Simple RLE decompression
|
|
105
|
+
*/
|
|
106
|
+
_rleDecompress(input) {
|
|
107
|
+
const output = [];
|
|
108
|
+
let i = 0;
|
|
109
|
+
|
|
110
|
+
while (i < input.length) {
|
|
111
|
+
if (input[i] === 0xFF && i + 2 < input.length) {
|
|
112
|
+
const count = input[i + 1];
|
|
113
|
+
const byte = input[i + 2];
|
|
114
|
+
for (let j = 0; j < count; j++) {
|
|
115
|
+
output.push(byte);
|
|
116
|
+
}
|
|
117
|
+
i += 3;
|
|
118
|
+
} else {
|
|
119
|
+
output.push(input[i]);
|
|
120
|
+
i++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Buffer.from(output);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getStats() {
|
|
128
|
+
return this.stats;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default CompressionPlugin;
|