@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.
@@ -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;