@synet/encoder 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/MANIFESTO.md +220 -0
- package/MANUAL.md +735 -0
- package/README.md +235 -0
- package/biome.json +37 -0
- package/dist/encoder.unit.d.ts +137 -0
- package/dist/encoder.unit.js +517 -0
- package/dist/functions.d.ts +74 -0
- package/dist/functions.js +243 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +38 -0
- package/dist/result.d.ts +23 -0
- package/dist/result.js +69 -0
- package/package.json +55 -0
- package/src/encoder.unit.ts +654 -0
- package/src/functions.ts +252 -0
- package/src/index.ts +49 -0
- package/src/result.ts +81 -0
- package/test/encoder-unit.test.ts +424 -0
- package/test/functions.test.ts +386 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +12 -0
@@ -0,0 +1,517 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* Encoder Unit - Conscious Encoding/Decoding Operations
|
4
|
+
*
|
5
|
+
* SYNET Unit Architecture v1.0.6 Implementation
|
6
|
+
*
|
7
|
+
* Philosophy: One unit, one goal - reliable data transformation
|
8
|
+
*
|
9
|
+
* Native Capabilities:
|
10
|
+
* - encode() - Transform data to specified format
|
11
|
+
* - decode() - Reverse transformation with validation
|
12
|
+
* - detect() - Auto-detect encoding format
|
13
|
+
* - validate() - Verify encoded data integrity
|
14
|
+
* - chain() - Sequential encoding operations
|
15
|
+
*
|
16
|
+
* Supported Formats: Base64, Base64URL, Hex, URI, ASCII
|
17
|
+
*
|
18
|
+
* @author SYNET ALPHA
|
19
|
+
* @version 1.0.0
|
20
|
+
* @follows Unit Architecture Doctrine v1.0.6
|
21
|
+
*/
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
23
|
+
exports.Encoder = void 0;
|
24
|
+
const unit_1 = require("@synet/unit");
|
25
|
+
const result_js_1 = require("./result.js");
|
26
|
+
/**
|
27
|
+
* Encoder Implementation
|
28
|
+
*
|
29
|
+
* Doctrine #1: ZERO DEPENDENCY (only Node.js/browser native APIs)
|
30
|
+
* Doctrine #17: VALUE OBJECT FOUNDATION (immutable with identity and capabilities)
|
31
|
+
*/
|
32
|
+
class Encoder extends unit_1.Unit {
|
33
|
+
// Doctrine #4: CREATE NOT CONSTRUCT (protected constructor)
|
34
|
+
constructor(props) {
|
35
|
+
super(props);
|
36
|
+
}
|
37
|
+
// Doctrine #4: CREATE NOT CONSTRUCT (static create with validation)
|
38
|
+
static create(config = {}) {
|
39
|
+
// Doctrine #3: PROPS CONTAIN EVERYTHING (validate and transform config to props)
|
40
|
+
const props = {
|
41
|
+
// Doctrine #7: EVERY UNIT MUST HAVE DNA
|
42
|
+
dna: (0, unit_1.createUnitSchema)({
|
43
|
+
id: 'encoder',
|
44
|
+
version: '1.0.0'
|
45
|
+
}),
|
46
|
+
defaultFormat: config.defaultFormat || 'base64',
|
47
|
+
strictMode: config.strictMode ?? false,
|
48
|
+
autoDetect: config.autoDetect ?? true,
|
49
|
+
maxInputSize: config.maxInputSize || 10 * 1024 * 1024, // 10MB default
|
50
|
+
validateOutput: config.validateOutput ?? true,
|
51
|
+
created: new Date()
|
52
|
+
};
|
53
|
+
return new Encoder(props);
|
54
|
+
}
|
55
|
+
// Doctrine #11: ALWAYS HELP (living documentation)
|
56
|
+
help() {
|
57
|
+
console.log(`
|
58
|
+
|
59
|
+
|
60
|
+
Hi, I am Encoder Unit [${this.dna.id}] v${this.dna.version} - Data Transformation Service
|
61
|
+
|
62
|
+
IDENTITY: ${this.whoami()}
|
63
|
+
DEFAULT FORMAT: ${this.props.defaultFormat}
|
64
|
+
STRICT MODE: ${this.props.strictMode}
|
65
|
+
AUTO DETECT: ${this.props.autoDetect}
|
66
|
+
MAX INPUT: ${(this.props.maxInputSize / 1024 / 1024).toFixed(1)}MB
|
67
|
+
STATUS: IMMUTABLE (stateless operations)
|
68
|
+
|
69
|
+
NATIVE CAPABILITIES:
|
70
|
+
• encode(data, format?) - Transform data to specified format (Result for validation)
|
71
|
+
• decode(data, format?) - Reverse transformation with auto-detection (Result)
|
72
|
+
• detect(data) - Auto-detect encoding format with confidence (throws on error)
|
73
|
+
• validate(data, format) - Verify encoded data integrity (throws on error)
|
74
|
+
• chain(data, formats) - Sequential encoding operations (Result)
|
75
|
+
• reverse(data, formats) - Reverse sequential decoding (Result)
|
76
|
+
|
77
|
+
SUPPORTED FORMATS:
|
78
|
+
• base64: Standard Base64 encoding (RFC 4648)
|
79
|
+
• base64url: URL-safe Base64 encoding
|
80
|
+
• hex: Hexadecimal encoding (lowercase)
|
81
|
+
• uri: URI component encoding
|
82
|
+
• ascii: ASCII text encoding
|
83
|
+
|
84
|
+
USAGE EXAMPLES:
|
85
|
+
const encoder = Encoder.create();
|
86
|
+
|
87
|
+
// Simple operations (Result pattern for validation)
|
88
|
+
const encoded = encoder.encode('Hello World', 'base64');
|
89
|
+
if (encoded.isSuccess) {
|
90
|
+
console.log(encoded.value.encoded); // SGVsbG8gV29ybGQ=
|
91
|
+
}
|
92
|
+
|
93
|
+
// Auto-detection decoding
|
94
|
+
const decoded = encoder.decode('SGVsbG8gV29ybGQ=');
|
95
|
+
if (decoded.isSuccess) {
|
96
|
+
console.log(decoded.value.decoded); // Hello World
|
97
|
+
}
|
98
|
+
|
99
|
+
// Format detection
|
100
|
+
const format = encoder.detect('48656c6c6f'); // hex
|
101
|
+
console.log(format.format); // 'hex'
|
102
|
+
|
103
|
+
LEARNING CAPABILITIES:
|
104
|
+
Other units can learn from me:
|
105
|
+
unit.learn([encoder.teach()]);
|
106
|
+
unit.execute('encoder.encode', data, format);
|
107
|
+
|
108
|
+
I TEACH:
|
109
|
+
• encode(data, format) - Encoding capability
|
110
|
+
• decode(data, format) - Decoding capability
|
111
|
+
• detect(data) - Format detection capability
|
112
|
+
• validate(data, format) - Validation capability
|
113
|
+
• chain(data, formats) - Sequential encoding capability
|
114
|
+
|
115
|
+
`);
|
116
|
+
}
|
117
|
+
// Doctrine #2: TEACH/LEARN PARADIGM (every unit must teach)
|
118
|
+
// Doctrine #9: ALWAYS TEACH (explicit capability binding)
|
119
|
+
// Doctrine #19: CAPABILITY LEAKAGE PREVENTION (teach only native capabilities)
|
120
|
+
teach() {
|
121
|
+
return {
|
122
|
+
// Doctrine #12: NAMESPACE EVERYTHING (unitId for namespacing)
|
123
|
+
unitId: this.dna.id,
|
124
|
+
capabilities: {
|
125
|
+
// Native encoding capabilities only - wrapped for unknown[] compatibility
|
126
|
+
encode: ((...args) => this.encode(args[0], args[1])),
|
127
|
+
decode: ((...args) => this.decode(args[0], args[1])),
|
128
|
+
detect: ((...args) => this.detect(args[0])),
|
129
|
+
validate: ((...args) => this.validate(args[0], args[1])),
|
130
|
+
chain: ((...args) => this.chain(args[0], args[1])),
|
131
|
+
// Metadata access
|
132
|
+
getDefaultFormat: (() => this.props.defaultFormat),
|
133
|
+
isStrictMode: (() => this.props.strictMode),
|
134
|
+
getMaxInputSize: (() => this.props.maxInputSize)
|
135
|
+
}
|
136
|
+
};
|
137
|
+
}
|
138
|
+
// Doctrine #14: ERROR BOUNDARY CLARITY (Result for complex operations)
|
139
|
+
/**
|
140
|
+
* Encode data to specified format (Result - complex validation operation)
|
141
|
+
*/
|
142
|
+
encode(data, format) {
|
143
|
+
try {
|
144
|
+
const encodingFormat = format || this.props.defaultFormat;
|
145
|
+
// Input validation
|
146
|
+
if (data.length > this.props.maxInputSize) {
|
147
|
+
return result_js_1.Result.fail(`Input too large: ${data.length} bytes (max: ${this.props.maxInputSize})`);
|
148
|
+
}
|
149
|
+
if (this.props.strictMode && !this.isValidInput(data)) {
|
150
|
+
return result_js_1.Result.fail('Invalid input for strict mode');
|
151
|
+
}
|
152
|
+
const encoded = this.performEncode(data, encodingFormat);
|
153
|
+
// Output validation if enabled
|
154
|
+
if (this.props.validateOutput) {
|
155
|
+
const validation = this.validate(encoded, encodingFormat);
|
156
|
+
if (!validation.isValid) {
|
157
|
+
return result_js_1.Result.fail(`Output validation failed: ${validation.errors.join(', ')}`);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
const result = {
|
161
|
+
encoded,
|
162
|
+
originalSize: data.length,
|
163
|
+
encodedSize: encoded.length,
|
164
|
+
format: encodingFormat,
|
165
|
+
compressionRatio: encoded.length / data.length,
|
166
|
+
timestamp: new Date()
|
167
|
+
};
|
168
|
+
return result_js_1.Result.success(result);
|
169
|
+
}
|
170
|
+
catch (error) {
|
171
|
+
return result_js_1.Result.fail(`Encoding failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
172
|
+
}
|
173
|
+
}
|
174
|
+
/**
|
175
|
+
* Decode data with optional format hint (Result - complex auto-detection operation)
|
176
|
+
*/
|
177
|
+
decode(data, format) {
|
178
|
+
try {
|
179
|
+
// Auto-detect format if not provided and auto-detection is enabled
|
180
|
+
let detectedFormat;
|
181
|
+
if (format) {
|
182
|
+
detectedFormat = format;
|
183
|
+
}
|
184
|
+
else if (this.props.autoDetect) {
|
185
|
+
detectedFormat = this.detect(data).format;
|
186
|
+
}
|
187
|
+
else {
|
188
|
+
detectedFormat = this.props.defaultFormat;
|
189
|
+
}
|
190
|
+
// Input validation
|
191
|
+
const validation = this.validate(data, detectedFormat);
|
192
|
+
if (!validation.isValid) {
|
193
|
+
if (this.props.strictMode) {
|
194
|
+
return result_js_1.Result.fail(`Invalid ${detectedFormat} format: ${validation.errors.join(', ')}`);
|
195
|
+
}
|
196
|
+
// In non-strict mode, still fail if the format is completely wrong
|
197
|
+
if (validation.errors.some(err => err.includes('invalid') || err.includes('Invalid'))) {
|
198
|
+
return result_js_1.Result.fail(`Invalid ${detectedFormat} format: ${validation.errors.join(', ')}`, new Error(`Validation failed for ${detectedFormat}`));
|
199
|
+
}
|
200
|
+
}
|
201
|
+
const decoded = this.performDecode(data, detectedFormat);
|
202
|
+
const result = {
|
203
|
+
decoded,
|
204
|
+
detectedFormat,
|
205
|
+
isValid: validation.isValid,
|
206
|
+
originalSize: data.length,
|
207
|
+
timestamp: new Date()
|
208
|
+
};
|
209
|
+
return result_js_1.Result.success(result);
|
210
|
+
}
|
211
|
+
catch (error) {
|
212
|
+
return result_js_1.Result.fail(`Decoding failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
/**
|
216
|
+
* Chain multiple encoding operations (Result - complex multi-step operation)
|
217
|
+
*/
|
218
|
+
chain(data, formats) {
|
219
|
+
try {
|
220
|
+
let result = data;
|
221
|
+
let totalRatio = 1;
|
222
|
+
for (const format of formats) {
|
223
|
+
const encoded = this.encode(result, format);
|
224
|
+
if (encoded.isFailure) {
|
225
|
+
return result_js_1.Result.fail(`Chain failed at ${format}: ${encoded.error}`);
|
226
|
+
}
|
227
|
+
result = encoded.value.encoded;
|
228
|
+
totalRatio *= encoded.value.compressionRatio;
|
229
|
+
}
|
230
|
+
const chainResult = {
|
231
|
+
encoded: result,
|
232
|
+
originalSize: data.length,
|
233
|
+
encodedSize: result.length,
|
234
|
+
format: formats[formats.length - 1], // Final format
|
235
|
+
compressionRatio: totalRatio,
|
236
|
+
timestamp: new Date()
|
237
|
+
};
|
238
|
+
return result_js_1.Result.success(chainResult);
|
239
|
+
}
|
240
|
+
catch (error) {
|
241
|
+
return result_js_1.Result.fail(`Chain encoding failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
/**
|
245
|
+
* Reverse chain decoding (Result - complex multi-step operation)
|
246
|
+
*/
|
247
|
+
reverse(data, formats) {
|
248
|
+
try {
|
249
|
+
let result = data;
|
250
|
+
const reversedFormats = [...formats].reverse();
|
251
|
+
for (const format of reversedFormats) {
|
252
|
+
const decoded = this.decode(result, format);
|
253
|
+
if (decoded.isFailure) {
|
254
|
+
return result_js_1.Result.fail(`Reverse chain failed at ${format}: ${decoded.error}`);
|
255
|
+
}
|
256
|
+
result = decoded.value.decoded;
|
257
|
+
}
|
258
|
+
const reverseResult = {
|
259
|
+
decoded: result,
|
260
|
+
detectedFormat: formats[0], // Original format
|
261
|
+
isValid: true,
|
262
|
+
originalSize: data.length,
|
263
|
+
timestamp: new Date()
|
264
|
+
};
|
265
|
+
return result_js_1.Result.success(reverseResult);
|
266
|
+
}
|
267
|
+
catch (error) {
|
268
|
+
return result_js_1.Result.fail(`Reverse chain failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
// Doctrine #14: ERROR BOUNDARY CLARITY (throws for simple operations)
|
272
|
+
/**
|
273
|
+
* Auto-detect encoding format (throw on error - simple classification operation)
|
274
|
+
*/
|
275
|
+
detect(data) {
|
276
|
+
const patterns = [
|
277
|
+
{
|
278
|
+
format: 'hex',
|
279
|
+
test: (s) => /^[0-9a-fA-F]+$/.test(s) && s.length % 2 === 0,
|
280
|
+
confidence: 0.95,
|
281
|
+
reason: 'Matches hexadecimal pattern with even length'
|
282
|
+
},
|
283
|
+
{
|
284
|
+
format: 'base64url',
|
285
|
+
test: (s) => /^[A-Za-z0-9\-_]*$/.test(s) && !s.includes('+') && !s.includes('/') && !s.includes('=') && s.length > 0,
|
286
|
+
confidence: 0.9,
|
287
|
+
reason: 'Matches base64url character set (URL-safe, no padding)'
|
288
|
+
},
|
289
|
+
{
|
290
|
+
format: 'base64',
|
291
|
+
test: (s) => /^[A-Za-z0-9+/]*={0,2}$/.test(s) && s.length % 4 === 0 && (s.includes('+') || s.includes('/') || s.includes('=')),
|
292
|
+
confidence: 0.85,
|
293
|
+
reason: 'Matches base64 character set with standard chars or padding'
|
294
|
+
},
|
295
|
+
{
|
296
|
+
format: 'uri',
|
297
|
+
test: (s) => s.includes('%') && /^[A-Za-z0-9\-_.~%!*'()]+$/.test(s),
|
298
|
+
confidence: 0.8,
|
299
|
+
reason: 'Contains URI percent-encoding characters'
|
300
|
+
},
|
301
|
+
{
|
302
|
+
format: 'ascii',
|
303
|
+
test: (s) => /^[\x20-\x7E]*$/.test(s),
|
304
|
+
confidence: 0.7,
|
305
|
+
reason: 'Contains only printable ASCII characters'
|
306
|
+
}
|
307
|
+
];
|
308
|
+
const matches = patterns.filter(p => p.test(data));
|
309
|
+
if (matches.length === 0) {
|
310
|
+
throw new Error(`Cannot detect encoding format for data: ${data.slice(0, 50)}...`);
|
311
|
+
}
|
312
|
+
// Return highest confidence match
|
313
|
+
const bestMatch = matches.reduce((best, current) => current.confidence > best.confidence ? current : best);
|
314
|
+
return {
|
315
|
+
format: bestMatch.format,
|
316
|
+
confidence: bestMatch.confidence,
|
317
|
+
reasons: matches.map(m => m.reason),
|
318
|
+
timestamp: new Date()
|
319
|
+
};
|
320
|
+
}
|
321
|
+
/**
|
322
|
+
* Validate encoded data format (throw on error - simple validation operation)
|
323
|
+
*/
|
324
|
+
validate(data, format) {
|
325
|
+
const errors = [];
|
326
|
+
const suggestions = [];
|
327
|
+
try {
|
328
|
+
switch (format) {
|
329
|
+
case 'base64':
|
330
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(data)) {
|
331
|
+
errors.push('Contains invalid base64 characters');
|
332
|
+
suggestions.push('Remove invalid characters or try base64url format');
|
333
|
+
}
|
334
|
+
if (data.length % 4 !== 0) {
|
335
|
+
errors.push('Invalid base64 length (must be multiple of 4)');
|
336
|
+
suggestions.push('Add padding with = characters');
|
337
|
+
}
|
338
|
+
break;
|
339
|
+
case 'base64url':
|
340
|
+
if (!/^[A-Za-z0-9\-_]*$/.test(data)) {
|
341
|
+
errors.push('Contains invalid base64url characters');
|
342
|
+
suggestions.push('Use only A-Z, a-z, 0-9, -, _ characters');
|
343
|
+
}
|
344
|
+
break;
|
345
|
+
case 'hex':
|
346
|
+
if (!/^[0-9a-fA-F]*$/.test(data)) {
|
347
|
+
errors.push('Contains invalid hexadecimal characters');
|
348
|
+
suggestions.push('Use only 0-9, a-f, A-F characters');
|
349
|
+
}
|
350
|
+
if (data.length % 2 !== 0) {
|
351
|
+
errors.push('Invalid hex length (must be even)');
|
352
|
+
suggestions.push('Add leading zero or remove extra character');
|
353
|
+
}
|
354
|
+
break;
|
355
|
+
case 'uri':
|
356
|
+
try {
|
357
|
+
decodeURIComponent(data);
|
358
|
+
}
|
359
|
+
catch {
|
360
|
+
errors.push('Invalid URI encoding');
|
361
|
+
suggestions.push('Check percent-encoding format');
|
362
|
+
}
|
363
|
+
break;
|
364
|
+
case 'ascii':
|
365
|
+
if (!/^[\x20-\x7E]*$/.test(data)) {
|
366
|
+
errors.push('Contains non-ASCII characters');
|
367
|
+
suggestions.push('Use only printable ASCII characters (32-126)');
|
368
|
+
}
|
369
|
+
break;
|
370
|
+
default:
|
371
|
+
throw new Error(`Unknown format: ${format}`);
|
372
|
+
}
|
373
|
+
return {
|
374
|
+
isValid: errors.length === 0,
|
375
|
+
format,
|
376
|
+
errors,
|
377
|
+
suggestions
|
378
|
+
};
|
379
|
+
}
|
380
|
+
catch (error) {
|
381
|
+
throw new Error(`Validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
382
|
+
}
|
383
|
+
}
|
384
|
+
// Doctrine #8: PURE FUNCTION HEARTS (core logic as pure functions)
|
385
|
+
performEncode(data, format) {
|
386
|
+
switch (format) {
|
387
|
+
case 'base64':
|
388
|
+
return this.encodeBase64(data);
|
389
|
+
case 'base64url':
|
390
|
+
return this.encodeBase64URL(data);
|
391
|
+
case 'hex':
|
392
|
+
return this.encodeHex(data);
|
393
|
+
case 'uri':
|
394
|
+
return this.encodeURI(data);
|
395
|
+
case 'ascii':
|
396
|
+
return this.encodeASCII(data);
|
397
|
+
default:
|
398
|
+
throw new Error(`Unsupported encoding format: ${format}`);
|
399
|
+
}
|
400
|
+
}
|
401
|
+
performDecode(data, format) {
|
402
|
+
switch (format) {
|
403
|
+
case 'base64':
|
404
|
+
return this.decodeBase64(data);
|
405
|
+
case 'base64url':
|
406
|
+
return this.decodeBase64URL(data);
|
407
|
+
case 'hex':
|
408
|
+
return this.decodeHex(data);
|
409
|
+
case 'uri':
|
410
|
+
return this.decodeURI(data);
|
411
|
+
case 'ascii':
|
412
|
+
return this.decodeASCII(data);
|
413
|
+
default:
|
414
|
+
throw new Error(`Unsupported decoding format: ${format}`);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
// Base64 implementation (cross-platform Node.js/Browser)
|
418
|
+
encodeBase64(data) {
|
419
|
+
if (typeof Buffer !== 'undefined') {
|
420
|
+
return Buffer.from(data, 'utf8').toString('base64');
|
421
|
+
}
|
422
|
+
if (typeof btoa !== 'undefined') {
|
423
|
+
// Modern Unicode-safe base64 encoding without deprecated unescape()
|
424
|
+
const bytes = new TextEncoder().encode(data);
|
425
|
+
const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
|
426
|
+
return btoa(binaryString);
|
427
|
+
}
|
428
|
+
throw new Error('No base64 encoding available');
|
429
|
+
}
|
430
|
+
decodeBase64(data) {
|
431
|
+
if (typeof Buffer !== 'undefined') {
|
432
|
+
return Buffer.from(data, 'base64').toString('utf8');
|
433
|
+
}
|
434
|
+
if (typeof atob !== 'undefined') {
|
435
|
+
// Modern Unicode-safe base64 decoding without deprecated escape()
|
436
|
+
const binaryString = atob(data);
|
437
|
+
const bytes = new Uint8Array(binaryString.length);
|
438
|
+
for (let i = 0; i < binaryString.length; i++) {
|
439
|
+
bytes[i] = binaryString.charCodeAt(i);
|
440
|
+
}
|
441
|
+
return new TextDecoder('utf-8').decode(bytes);
|
442
|
+
}
|
443
|
+
throw new Error('No base64 decoding available');
|
444
|
+
}
|
445
|
+
// Base64URL implementation
|
446
|
+
encodeBase64URL(data) {
|
447
|
+
return this.encodeBase64(data)
|
448
|
+
.replace(/\+/g, '-')
|
449
|
+
.replace(/\//g, '_')
|
450
|
+
.replace(/=/g, '');
|
451
|
+
}
|
452
|
+
decodeBase64URL(data) {
|
453
|
+
let base64 = data.replace(/-/g, '+').replace(/_/g, '/');
|
454
|
+
while (base64.length % 4) {
|
455
|
+
base64 += '=';
|
456
|
+
}
|
457
|
+
return this.decodeBase64(base64);
|
458
|
+
}
|
459
|
+
// Hex implementation
|
460
|
+
encodeHex(data) {
|
461
|
+
return Array.from(data)
|
462
|
+
.map(char => char.charCodeAt(0).toString(16).padStart(2, '0'))
|
463
|
+
.join('');
|
464
|
+
}
|
465
|
+
decodeHex(data) {
|
466
|
+
const hex = data.replace(/[^0-9a-fA-F]/g, '');
|
467
|
+
let result = '';
|
468
|
+
for (let i = 0; i < hex.length; i += 2) {
|
469
|
+
result += String.fromCharCode(Number.parseInt(hex.slice(i, i + 2), 16));
|
470
|
+
}
|
471
|
+
return result;
|
472
|
+
}
|
473
|
+
// URI implementation
|
474
|
+
encodeURI(data) {
|
475
|
+
return encodeURIComponent(data);
|
476
|
+
}
|
477
|
+
decodeURI(data) {
|
478
|
+
return decodeURIComponent(data);
|
479
|
+
}
|
480
|
+
// ASCII implementation
|
481
|
+
encodeASCII(data) {
|
482
|
+
return Array.from(data)
|
483
|
+
.map(char => {
|
484
|
+
const code = char.charCodeAt(0);
|
485
|
+
if (code > 127) {
|
486
|
+
throw new Error(`Non-ASCII character found: ${char} (code: ${code})`);
|
487
|
+
}
|
488
|
+
return char;
|
489
|
+
})
|
490
|
+
.join('');
|
491
|
+
}
|
492
|
+
decodeASCII(data) {
|
493
|
+
return data; // ASCII is already decoded
|
494
|
+
}
|
495
|
+
// Utility methods
|
496
|
+
isValidInput(data) {
|
497
|
+
// Basic input validation for strict mode
|
498
|
+
return typeof data === 'string' && data.length > 0;
|
499
|
+
}
|
500
|
+
// Standard unit identification
|
501
|
+
whoami() {
|
502
|
+
return `EncoderUnit[${this.dna.id}@${this.dna.version}]`;
|
503
|
+
}
|
504
|
+
// JSON serialization (no sensitive data exposed)
|
505
|
+
toJSON() {
|
506
|
+
return {
|
507
|
+
type: 'EncoderUnit',
|
508
|
+
dna: this.dna,
|
509
|
+
defaultFormat: this.props.defaultFormat,
|
510
|
+
strictMode: this.props.strictMode,
|
511
|
+
autoDetect: this.props.autoDetect,
|
512
|
+
learnedCapabilities: this.capabilities(), // This calls the base Unit class method
|
513
|
+
created: this.props.created
|
514
|
+
};
|
515
|
+
}
|
516
|
+
}
|
517
|
+
exports.Encoder = Encoder;
|
@@ -0,0 +1,74 @@
|
|
1
|
+
/**
|
2
|
+
* Pure Encoding Functions - Serverless Ready
|
3
|
+
*
|
4
|
+
* Simple, stateless functions for encoding/decoding operations.
|
5
|
+
* These throw on error (simple operations) following Doctrine #14.
|
6
|
+
*/
|
7
|
+
/**
|
8
|
+
* Encoding format types
|
9
|
+
*/
|
10
|
+
export type EncodingFormat = 'base64' | 'base64url' | 'hex' | 'uri' | 'ascii';
|
11
|
+
/**
|
12
|
+
* Encode string to Base64
|
13
|
+
*/
|
14
|
+
export declare function encodeBase64(data: string): string;
|
15
|
+
/**
|
16
|
+
* Decode Base64 string
|
17
|
+
*/
|
18
|
+
export declare function decodeBase64(data: string): string;
|
19
|
+
/**
|
20
|
+
* Encode string to Base64URL (URL-safe)
|
21
|
+
*/
|
22
|
+
export declare function encodeBase64URL(data: string): string;
|
23
|
+
/**
|
24
|
+
* Decode Base64URL string
|
25
|
+
*/
|
26
|
+
export declare function decodeBase64URL(data: string): string;
|
27
|
+
/**
|
28
|
+
* Encode string to hexadecimal
|
29
|
+
*/
|
30
|
+
export declare function encodeHex(data: string): string;
|
31
|
+
/**
|
32
|
+
* Decode hexadecimal string
|
33
|
+
*/
|
34
|
+
export declare function decodeHex(data: string): string;
|
35
|
+
/**
|
36
|
+
* Encode string for URI
|
37
|
+
*/
|
38
|
+
export declare function encodeURIString(data: string): string;
|
39
|
+
/**
|
40
|
+
* Decode URI-encoded string
|
41
|
+
*/
|
42
|
+
export declare function decodeURIString(data: string): string;
|
43
|
+
/**
|
44
|
+
* Encode string as ASCII (validates ASCII-only)
|
45
|
+
*/
|
46
|
+
export declare function encodeASCII(data: string): string;
|
47
|
+
/**
|
48
|
+
* Decode ASCII string (no-op, validates printable ASCII)
|
49
|
+
*/
|
50
|
+
export declare function decodeASCII(data: string): string;
|
51
|
+
/**
|
52
|
+
* Generic encode function
|
53
|
+
*/
|
54
|
+
export declare function encode(data: string, format: EncodingFormat): string;
|
55
|
+
/**
|
56
|
+
* Generic decode function
|
57
|
+
*/
|
58
|
+
export declare function decode(data: string, format: EncodingFormat): string;
|
59
|
+
/**
|
60
|
+
* Auto-detect encoding format
|
61
|
+
*/
|
62
|
+
export declare function detectFormat(data: string): EncodingFormat;
|
63
|
+
/**
|
64
|
+
* Validate format of encoded data
|
65
|
+
*/
|
66
|
+
export declare function validateFormat(data: string, format: EncodingFormat): boolean;
|
67
|
+
/**
|
68
|
+
* Chain multiple encodings
|
69
|
+
*/
|
70
|
+
export declare function chain(data: string, formats: EncodingFormat[]): string;
|
71
|
+
/**
|
72
|
+
* Reverse chain decodings
|
73
|
+
*/
|
74
|
+
export declare function reverseChain(data: string, formats: EncodingFormat[]): string;
|