@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,243 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* Pure Encoding Functions - Serverless Ready
|
4
|
+
*
|
5
|
+
* Simple, stateless functions for encoding/decoding operations.
|
6
|
+
* These throw on error (simple operations) following Doctrine #14.
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.encodeBase64 = encodeBase64;
|
10
|
+
exports.decodeBase64 = decodeBase64;
|
11
|
+
exports.encodeBase64URL = encodeBase64URL;
|
12
|
+
exports.decodeBase64URL = decodeBase64URL;
|
13
|
+
exports.encodeHex = encodeHex;
|
14
|
+
exports.decodeHex = decodeHex;
|
15
|
+
exports.encodeURIString = encodeURIString;
|
16
|
+
exports.decodeURIString = decodeURIString;
|
17
|
+
exports.encodeASCII = encodeASCII;
|
18
|
+
exports.decodeASCII = decodeASCII;
|
19
|
+
exports.encode = encode;
|
20
|
+
exports.decode = decode;
|
21
|
+
exports.detectFormat = detectFormat;
|
22
|
+
exports.validateFormat = validateFormat;
|
23
|
+
exports.chain = chain;
|
24
|
+
exports.reverseChain = reverseChain;
|
25
|
+
/**
|
26
|
+
* Encode string to Base64
|
27
|
+
*/
|
28
|
+
function encodeBase64(data) {
|
29
|
+
if (typeof Buffer !== 'undefined') {
|
30
|
+
return Buffer.from(data, 'utf8').toString('base64');
|
31
|
+
}
|
32
|
+
if (typeof btoa !== 'undefined') {
|
33
|
+
// Modern Unicode-safe base64 encoding without deprecated unescape()
|
34
|
+
const bytes = new TextEncoder().encode(data);
|
35
|
+
const binaryString = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
|
36
|
+
return btoa(binaryString);
|
37
|
+
}
|
38
|
+
throw new Error('No base64 encoding available');
|
39
|
+
}
|
40
|
+
/**
|
41
|
+
* Decode Base64 string
|
42
|
+
*/
|
43
|
+
function decodeBase64(data) {
|
44
|
+
if (typeof Buffer !== 'undefined') {
|
45
|
+
return Buffer.from(data, 'base64').toString('utf8');
|
46
|
+
}
|
47
|
+
if (typeof atob !== 'undefined') {
|
48
|
+
// Modern Unicode-safe base64 decoding without deprecated escape()
|
49
|
+
const binaryString = atob(data);
|
50
|
+
const bytes = new Uint8Array(binaryString.length);
|
51
|
+
for (let i = 0; i < binaryString.length; i++) {
|
52
|
+
bytes[i] = binaryString.charCodeAt(i);
|
53
|
+
}
|
54
|
+
return new TextDecoder('utf-8').decode(bytes);
|
55
|
+
}
|
56
|
+
throw new Error('No base64 decoding available');
|
57
|
+
}
|
58
|
+
/**
|
59
|
+
* Encode string to Base64URL (URL-safe)
|
60
|
+
*/
|
61
|
+
function encodeBase64URL(data) {
|
62
|
+
return encodeBase64(data)
|
63
|
+
.replace(/\+/g, '-')
|
64
|
+
.replace(/\//g, '_')
|
65
|
+
.replace(/=/g, '');
|
66
|
+
}
|
67
|
+
/**
|
68
|
+
* Decode Base64URL string
|
69
|
+
*/
|
70
|
+
function decodeBase64URL(data) {
|
71
|
+
let base64 = data.replace(/-/g, '+').replace(/_/g, '/');
|
72
|
+
while (base64.length % 4) {
|
73
|
+
base64 += '=';
|
74
|
+
}
|
75
|
+
return decodeBase64(base64);
|
76
|
+
}
|
77
|
+
/**
|
78
|
+
* Encode string to hexadecimal
|
79
|
+
*/
|
80
|
+
function encodeHex(data) {
|
81
|
+
return Array.from(data)
|
82
|
+
.map(char => char.charCodeAt(0).toString(16).padStart(2, '0'))
|
83
|
+
.join('');
|
84
|
+
}
|
85
|
+
/**
|
86
|
+
* Decode hexadecimal string
|
87
|
+
*/
|
88
|
+
function decodeHex(data) {
|
89
|
+
if (data.length % 2 !== 0) {
|
90
|
+
throw new Error('Invalid hex string: length must be even');
|
91
|
+
}
|
92
|
+
if (!/^[0-9a-fA-F]*$/.test(data)) {
|
93
|
+
throw new Error('Invalid hex string: contains non-hex characters');
|
94
|
+
}
|
95
|
+
let result = '';
|
96
|
+
for (let i = 0; i < data.length; i += 2) {
|
97
|
+
result += String.fromCharCode(Number.parseInt(data.slice(i, i + 2), 16));
|
98
|
+
}
|
99
|
+
return result;
|
100
|
+
}
|
101
|
+
/**
|
102
|
+
* Encode string for URI
|
103
|
+
*/
|
104
|
+
function encodeURIString(data) {
|
105
|
+
return encodeURIComponent(data);
|
106
|
+
}
|
107
|
+
/**
|
108
|
+
* Decode URI-encoded string
|
109
|
+
*/
|
110
|
+
function decodeURIString(data) {
|
111
|
+
return decodeURIComponent(data);
|
112
|
+
}
|
113
|
+
/**
|
114
|
+
* Encode string as ASCII (validates ASCII-only)
|
115
|
+
*/
|
116
|
+
function encodeASCII(data) {
|
117
|
+
for (let i = 0; i < data.length; i++) {
|
118
|
+
const code = data.charCodeAt(i);
|
119
|
+
if (code > 127) {
|
120
|
+
throw new Error(`Non-ASCII character found at position ${i}: '${data[i]}' (code: ${code})`);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
return data;
|
124
|
+
}
|
125
|
+
/**
|
126
|
+
* Decode ASCII string (no-op, validates printable ASCII)
|
127
|
+
*/
|
128
|
+
function decodeASCII(data) {
|
129
|
+
for (let i = 0; i < data.length; i++) {
|
130
|
+
const code = data.charCodeAt(i);
|
131
|
+
if (code < 32 || code > 126) {
|
132
|
+
throw new Error(`Non-printable ASCII character at position ${i}: code ${code}`);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
return data;
|
136
|
+
}
|
137
|
+
/**
|
138
|
+
* Generic encode function
|
139
|
+
*/
|
140
|
+
function encode(data, format) {
|
141
|
+
switch (format) {
|
142
|
+
case 'base64':
|
143
|
+
return encodeBase64(data);
|
144
|
+
case 'base64url':
|
145
|
+
return encodeBase64URL(data);
|
146
|
+
case 'hex':
|
147
|
+
return encodeHex(data);
|
148
|
+
case 'uri':
|
149
|
+
return encodeURIString(data);
|
150
|
+
case 'ascii':
|
151
|
+
return encodeASCII(data);
|
152
|
+
default:
|
153
|
+
throw new Error(`Unsupported encoding format: ${format}`);
|
154
|
+
}
|
155
|
+
}
|
156
|
+
/**
|
157
|
+
* Generic decode function
|
158
|
+
*/
|
159
|
+
function decode(data, format) {
|
160
|
+
switch (format) {
|
161
|
+
case 'base64':
|
162
|
+
return decodeBase64(data);
|
163
|
+
case 'base64url':
|
164
|
+
return decodeBase64URL(data);
|
165
|
+
case 'hex':
|
166
|
+
return decodeHex(data);
|
167
|
+
case 'uri':
|
168
|
+
return decodeURIString(data);
|
169
|
+
case 'ascii':
|
170
|
+
return decodeASCII(data);
|
171
|
+
default:
|
172
|
+
throw new Error(`Unsupported decoding format: ${format}`);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
/**
|
176
|
+
* Auto-detect encoding format
|
177
|
+
*/
|
178
|
+
function detectFormat(data) {
|
179
|
+
// Test patterns in order of specificity/confidence
|
180
|
+
if (/^[0-9a-fA-F]+$/.test(data) && data.length % 2 === 0) {
|
181
|
+
return 'hex';
|
182
|
+
}
|
183
|
+
// Base64url should be tested before base64 since it's more restrictive
|
184
|
+
if (/^[A-Za-z0-9\-_]*$/.test(data) && !data.includes('+') && !data.includes('/') && !data.includes('=') && data.length > 0) {
|
185
|
+
return 'base64url';
|
186
|
+
}
|
187
|
+
// Base64 with standard characters or padding
|
188
|
+
if (/^[A-Za-z0-9+/]*={0,2}$/.test(data) && data.length % 4 === 0 && (data.includes('+') || data.includes('/') || data.includes('='))) {
|
189
|
+
return 'base64';
|
190
|
+
}
|
191
|
+
if (data.includes('%') && /^[A-Za-z0-9\-_.~%!*'()]+$/.test(data)) {
|
192
|
+
return 'uri';
|
193
|
+
}
|
194
|
+
if (/^[\x20-\x7E]*$/.test(data)) {
|
195
|
+
return 'ascii';
|
196
|
+
}
|
197
|
+
throw new Error(`Cannot detect encoding format for: ${data.slice(0, 50)}...`);
|
198
|
+
}
|
199
|
+
/**
|
200
|
+
* Validate format of encoded data
|
201
|
+
*/
|
202
|
+
function validateFormat(data, format) {
|
203
|
+
try {
|
204
|
+
switch (format) {
|
205
|
+
case 'base64':
|
206
|
+
return /^[A-Za-z0-9+/]*={0,2}$/.test(data) && data.length % 4 === 0;
|
207
|
+
case 'base64url':
|
208
|
+
return /^[A-Za-z0-9\-_]*$/.test(data);
|
209
|
+
case 'hex':
|
210
|
+
return /^[0-9a-fA-F]*$/.test(data) && data.length % 2 === 0;
|
211
|
+
case 'uri':
|
212
|
+
decodeURIComponent(data);
|
213
|
+
return true;
|
214
|
+
case 'ascii':
|
215
|
+
return /^[\x20-\x7E]*$/.test(data);
|
216
|
+
default:
|
217
|
+
return false;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
catch {
|
221
|
+
return false;
|
222
|
+
}
|
223
|
+
}
|
224
|
+
/**
|
225
|
+
* Chain multiple encodings
|
226
|
+
*/
|
227
|
+
function chain(data, formats) {
|
228
|
+
let result = data;
|
229
|
+
for (const format of formats) {
|
230
|
+
result = encode(result, format);
|
231
|
+
}
|
232
|
+
return result;
|
233
|
+
}
|
234
|
+
/**
|
235
|
+
* Reverse chain decodings
|
236
|
+
*/
|
237
|
+
function reverseChain(data, formats) {
|
238
|
+
let result = data;
|
239
|
+
for (const format of [...formats].reverse()) {
|
240
|
+
result = decode(result, format);
|
241
|
+
}
|
242
|
+
return result;
|
243
|
+
}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
/**
|
2
|
+
* @synet/encoder - Conscious Encoding/Decoding Unit
|
3
|
+
*
|
4
|
+
* Zero-dependency encoding operations following Unit Architecture doctrine.
|
5
|
+
*
|
6
|
+
* EXPORTS:
|
7
|
+
* - Encoder: Complete conscious encoder unit with teach/learn capabilities
|
8
|
+
* - Pure functions: Simple functional encoding operations
|
9
|
+
* - Result: Foundational error handling pattern
|
10
|
+
* - Types: All encoding-related interfaces
|
11
|
+
*/
|
12
|
+
export { Encoder } from './encoder.unit.js';
|
13
|
+
export { Result } from './result.js';
|
14
|
+
export type { EncoderConfig, EncoderProps, EncodingFormat, EncodingResult, DecodingResult, DetectionResult, ValidationResult } from './encoder.unit.js';
|
15
|
+
export { encode, decode, encodeBase64, decodeBase64, encodeBase64URL, decodeBase64URL, encodeHex, decodeHex, encodeURIString, decodeURIString, encodeASCII, decodeASCII, detectFormat, validateFormat, chain, reverseChain, type EncodingFormat as FunctionEncodingFormat } from './functions.js';
|
package/dist/index.js
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* @synet/encoder - Conscious Encoding/Decoding Unit
|
4
|
+
*
|
5
|
+
* Zero-dependency encoding operations following Unit Architecture doctrine.
|
6
|
+
*
|
7
|
+
* EXPORTS:
|
8
|
+
* - Encoder: Complete conscious encoder unit with teach/learn capabilities
|
9
|
+
* - Pure functions: Simple functional encoding operations
|
10
|
+
* - Result: Foundational error handling pattern
|
11
|
+
* - Types: All encoding-related interfaces
|
12
|
+
*/
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
14
|
+
exports.reverseChain = exports.chain = exports.validateFormat = exports.detectFormat = exports.decodeASCII = exports.encodeASCII = exports.decodeURIString = exports.encodeURIString = exports.decodeHex = exports.encodeHex = exports.decodeBase64URL = exports.encodeBase64URL = exports.decodeBase64 = exports.encodeBase64 = exports.decode = exports.encode = exports.Result = exports.Encoder = void 0;
|
15
|
+
// Core Unit
|
16
|
+
var encoder_unit_js_1 = require("./encoder.unit.js");
|
17
|
+
Object.defineProperty(exports, "Encoder", { enumerable: true, get: function () { return encoder_unit_js_1.Encoder; } });
|
18
|
+
// Result pattern (foundational)
|
19
|
+
var result_js_1 = require("./result.js");
|
20
|
+
Object.defineProperty(exports, "Result", { enumerable: true, get: function () { return result_js_1.Result; } });
|
21
|
+
// Pure function exports for simple use cases
|
22
|
+
var functions_js_1 = require("./functions.js");
|
23
|
+
Object.defineProperty(exports, "encode", { enumerable: true, get: function () { return functions_js_1.encode; } });
|
24
|
+
Object.defineProperty(exports, "decode", { enumerable: true, get: function () { return functions_js_1.decode; } });
|
25
|
+
Object.defineProperty(exports, "encodeBase64", { enumerable: true, get: function () { return functions_js_1.encodeBase64; } });
|
26
|
+
Object.defineProperty(exports, "decodeBase64", { enumerable: true, get: function () { return functions_js_1.decodeBase64; } });
|
27
|
+
Object.defineProperty(exports, "encodeBase64URL", { enumerable: true, get: function () { return functions_js_1.encodeBase64URL; } });
|
28
|
+
Object.defineProperty(exports, "decodeBase64URL", { enumerable: true, get: function () { return functions_js_1.decodeBase64URL; } });
|
29
|
+
Object.defineProperty(exports, "encodeHex", { enumerable: true, get: function () { return functions_js_1.encodeHex; } });
|
30
|
+
Object.defineProperty(exports, "decodeHex", { enumerable: true, get: function () { return functions_js_1.decodeHex; } });
|
31
|
+
Object.defineProperty(exports, "encodeURIString", { enumerable: true, get: function () { return functions_js_1.encodeURIString; } });
|
32
|
+
Object.defineProperty(exports, "decodeURIString", { enumerable: true, get: function () { return functions_js_1.decodeURIString; } });
|
33
|
+
Object.defineProperty(exports, "encodeASCII", { enumerable: true, get: function () { return functions_js_1.encodeASCII; } });
|
34
|
+
Object.defineProperty(exports, "decodeASCII", { enumerable: true, get: function () { return functions_js_1.decodeASCII; } });
|
35
|
+
Object.defineProperty(exports, "detectFormat", { enumerable: true, get: function () { return functions_js_1.detectFormat; } });
|
36
|
+
Object.defineProperty(exports, "validateFormat", { enumerable: true, get: function () { return functions_js_1.validateFormat; } });
|
37
|
+
Object.defineProperty(exports, "chain", { enumerable: true, get: function () { return functions_js_1.chain; } });
|
38
|
+
Object.defineProperty(exports, "reverseChain", { enumerable: true, get: function () { return functions_js_1.reverseChain; } });
|
package/dist/result.d.ts
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
/**
|
2
|
+
* Foundational Result Pattern for Encoder Operations
|
3
|
+
*
|
4
|
+
* Local copy following Unit Architecture Doctrine #14: ERROR BOUNDARY CLARITY
|
5
|
+
* Simple operations throw, complex operations use Result pattern
|
6
|
+
*/
|
7
|
+
export declare class Result<T> {
|
8
|
+
private readonly _value;
|
9
|
+
private readonly _error;
|
10
|
+
private readonly _errorCause?;
|
11
|
+
private constructor();
|
12
|
+
static success<T>(value: T): Result<T>;
|
13
|
+
static fail<T>(message: string, cause?: unknown): Result<T>;
|
14
|
+
get isSuccess(): boolean;
|
15
|
+
get isFailure(): boolean;
|
16
|
+
get value(): T;
|
17
|
+
get error(): string;
|
18
|
+
get errorCause(): unknown;
|
19
|
+
map<U>(fn: (value: T) => U): Result<U>;
|
20
|
+
flatMap<U>(fn: (value: T) => Result<U>): Result<U>;
|
21
|
+
getOrElse(defaultValue: T): T;
|
22
|
+
match<U>(onSuccess: (value: T) => U, onFailure: (error: string) => U): U;
|
23
|
+
}
|
package/dist/result.js
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* Foundational Result Pattern for Encoder Operations
|
4
|
+
*
|
5
|
+
* Local copy following Unit Architecture Doctrine #14: ERROR BOUNDARY CLARITY
|
6
|
+
* Simple operations throw, complex operations use Result pattern
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.Result = void 0;
|
10
|
+
class Result {
|
11
|
+
constructor(_value, _error, _errorCause) {
|
12
|
+
this._value = _value;
|
13
|
+
this._error = _error;
|
14
|
+
this._errorCause = _errorCause;
|
15
|
+
}
|
16
|
+
static success(value) {
|
17
|
+
return new Result(value, null);
|
18
|
+
}
|
19
|
+
static fail(message, cause) {
|
20
|
+
return new Result(null, message, cause);
|
21
|
+
}
|
22
|
+
get isSuccess() {
|
23
|
+
return this._error === null;
|
24
|
+
}
|
25
|
+
get isFailure() {
|
26
|
+
return this._error !== null;
|
27
|
+
}
|
28
|
+
get value() {
|
29
|
+
if (this._error !== null) {
|
30
|
+
throw new Error(`Attempted to get value from failed Result: ${this._error}`);
|
31
|
+
}
|
32
|
+
return this._value;
|
33
|
+
}
|
34
|
+
get error() {
|
35
|
+
return this._error || '';
|
36
|
+
}
|
37
|
+
get errorCause() {
|
38
|
+
return this._errorCause;
|
39
|
+
}
|
40
|
+
map(fn) {
|
41
|
+
if (this.isFailure) {
|
42
|
+
return Result.fail(this._error || 'Unknown error', this._errorCause);
|
43
|
+
}
|
44
|
+
try {
|
45
|
+
return Result.success(fn(this.value));
|
46
|
+
}
|
47
|
+
catch (error) {
|
48
|
+
return Result.fail(`Map operation failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
flatMap(fn) {
|
52
|
+
if (this.isFailure) {
|
53
|
+
return Result.fail(this._error || 'Unknown error', this._errorCause);
|
54
|
+
}
|
55
|
+
try {
|
56
|
+
return fn(this.value);
|
57
|
+
}
|
58
|
+
catch (error) {
|
59
|
+
return Result.fail(`FlatMap operation failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
getOrElse(defaultValue) {
|
63
|
+
return this.isSuccess ? this.value : defaultValue;
|
64
|
+
}
|
65
|
+
match(onSuccess, onFailure) {
|
66
|
+
return this.isSuccess ? onSuccess(this.value) : onFailure(this.error);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
exports.Result = Result;
|
package/package.json
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
{
|
2
|
+
"name": "@synet/encoder",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Unit Architecture compliant encoding/decoding operations",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"types": "dist/index.d.ts",
|
7
|
+
"private": false,
|
8
|
+
"publishConfig": {
|
9
|
+
"access": "public"
|
10
|
+
},
|
11
|
+
"repository": {
|
12
|
+
"type": "git",
|
13
|
+
"url": "git+https://github.com/synthetism/encoder.git"
|
14
|
+
},
|
15
|
+
"scripts": {
|
16
|
+
"build": "tsc",
|
17
|
+
"test": "vitest run",
|
18
|
+
"dev:test": "vitest",
|
19
|
+
"clean": "rm -rf dist",
|
20
|
+
"prebuild": "npm run clean",
|
21
|
+
"coverage": "vitest run --coverage",
|
22
|
+
"lint": "biome lint ./src",
|
23
|
+
"lint:fix": "biome lint --write ./src",
|
24
|
+
"format": "biome format --write './src'",
|
25
|
+
"prepublishOnly": "npm run lint:fix && npm run test && npm run build",
|
26
|
+
"version:dev": "npm version --no-git-tag-version prerelease --preid=dev",
|
27
|
+
"version:patch": "npm version --no-git-tag-version patch",
|
28
|
+
"version:minor": "npm version --no-git-tag-version minor",
|
29
|
+
"version:major": "npm version --no-git-tag-version major",
|
30
|
+
"publish:dev": "npm publish --registry=https://registry.dig.run/ --tag dev",
|
31
|
+
"publish:prod": "npm publish --registry=https://registry.npmjs.org/",
|
32
|
+
"release:dev": "npm run version:dev && npm run publish:dev",
|
33
|
+
"release:patch": "npm run version:patch && npm run publish:prod",
|
34
|
+
"release:minor": "npm run version:minor && npm run publish:prod",
|
35
|
+
"release:major": "npm run version:major && npm run publish:prod"
|
36
|
+
},
|
37
|
+
"devDependencies": {
|
38
|
+
"@biomejs/biome": "^1.9.4",
|
39
|
+
"@types/node": "^22.15.31",
|
40
|
+
"@vitest/coverage-v8": "^3.1.3",
|
41
|
+
"typescript": "^5.8.3",
|
42
|
+
"vitest": "^3.2.3"
|
43
|
+
},
|
44
|
+
"keywords": [
|
45
|
+
"Synet",
|
46
|
+
"Unit Architecture",
|
47
|
+
"Living beings in code"
|
48
|
+
],
|
49
|
+
"author": "Synet Team",
|
50
|
+
"homepage": "https://synthetism.ai",
|
51
|
+
"license": "MIT",
|
52
|
+
"dependencies": {
|
53
|
+
"@synet/unit": "^1.0.6"
|
54
|
+
}
|
55
|
+
}
|