@aperturesyndicate/synx-format 3.6.0 → 3.6.1
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 +5 -5
- package/SPECIFICATION.md +3 -2
- package/bin/synx.js +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/calc.d.ts +1 -1
- package/dist/calc.js +1 -1
- package/dist/engine.d.ts +1 -1
- package/dist/engine.js +1 -1
- package/dist/index.d.ts +93 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +323 -3
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +13 -2
- package/dist/parser.js.map +1 -1
- package/dist/synx.browser.js +20 -20
- package/dist/synx.browser.js.map +3 -3
- package/dist/synx.browser.mjs +20 -20
- package/dist/synx.browser.mjs.map +3 -3
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# SYNX for JS/TS — @aperturesyndicate/synx
|
|
1
|
+
# SYNX for JS/TS — @aperturesyndicate/synx-format
|
|
2
2
|
|
|
3
3
|
The official JavaScript & TypeScript parser for the SYNX format.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @aperturesyndicate/synx
|
|
8
|
+
npm install @aperturesyndicate/synx-format
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```javascript
|
|
14
|
-
const Synx = require('@aperturesyndicate/synx');
|
|
14
|
+
const Synx = require('@aperturesyndicate/synx-format');
|
|
15
15
|
|
|
16
16
|
// Load from file
|
|
17
17
|
const data = Synx.loadSync('config.synx');
|
|
@@ -25,7 +25,7 @@ console.log(data2.name); // "Wario"
|
|
|
25
25
|
### TypeScript
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
|
-
import Synx from '@aperturesyndicate/synx';
|
|
28
|
+
import Synx from '@aperturesyndicate/synx-format';
|
|
29
29
|
|
|
30
30
|
interface Config {
|
|
31
31
|
server: { port: number; host: string };
|
|
@@ -63,7 +63,7 @@ console.log(data.server.port); // typed as number
|
|
|
63
63
|
Install globally:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
npm install -g @aperturesyndicate/synx
|
|
66
|
+
npm install -g @aperturesyndicate/synx-format
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Commands
|
package/SPECIFICATION.md
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
The normative specification files live in the monorepo (not duplicated here for npm):
|
|
4
4
|
|
|
5
|
-
- English: [`docs/spec/SPECIFICATION_EN.md`](https://github.com/
|
|
6
|
-
- Russian: [`docs/spec/SPECIFICATION_RU.md`](https://github.com/
|
|
5
|
+
- English: [`docs/spec/SPECIFICATION_EN.md`](https://github.com/APERTURESyndicate/synx-format/blob/main/docs/spec/SPECIFICATION_EN.md)
|
|
6
|
+
- Russian: [`docs/spec/SPECIFICATION_RU.md`](https://github.com/APERTURESyndicate/synx-format/blob/main/docs/spec/SPECIFICATION_RU.md)
|
|
7
|
+
- Normative (SYNX 3.6): [`docs/spec/SYNX-3.6-NORMATIVE.md`](https://github.com/APERTURESyndicate/synx-format/blob/main/docs/spec/SYNX-3.6-NORMATIVE.md)
|
package/bin/synx.js
CHANGED
|
@@ -14,7 +14,7 @@ SYNX CLI — The Active Data Format (v3.6.0)
|
|
|
14
14
|
|
|
15
15
|
DEPRECATED: This Node.js CLI is superseded by the native Rust CLI.
|
|
16
16
|
Install: cargo install synx-cli
|
|
17
|
-
Docs: https://github.com/
|
|
17
|
+
Docs: https://github.com/APERTURESyndicate/synx-format#cli-rust
|
|
18
18
|
|
|
19
19
|
Usage:
|
|
20
20
|
synx convert <file.synx> [--format json|yaml|toml|env] [--strict] [--active]
|
package/dist/browser.d.ts
CHANGED
package/dist/browser.js
CHANGED
package/dist/calc.d.ts
CHANGED
package/dist/calc.js
CHANGED
package/dist/engine.d.ts
CHANGED
package/dist/engine.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SYNX — @aperturesyndicate/synx
|
|
2
|
+
* SYNX — @aperturesyndicate/synx-format
|
|
3
3
|
*
|
|
4
4
|
* The Active Data Format.
|
|
5
5
|
* Faster than JSON. Cheaper for AI tokens. Built-in logic.
|
|
@@ -54,6 +54,32 @@ declare class Synx {
|
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
56
|
static load<T extends SynxObject = SynxObject>(filePath: string, options?: SynxOptions): Promise<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Save a JS object to a .synx file synchronously.
|
|
59
|
+
*
|
|
60
|
+
* @param filePath - Path to the .synx file.
|
|
61
|
+
* @param obj - The object to serialize and save.
|
|
62
|
+
* @param active - If true, include `!active` directive.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* Synx.saveSync('config.synx', { app_name: 'TotalWario', port: 8080 });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
static saveSync(filePath: string, obj: SynxObject, active?: boolean): void;
|
|
70
|
+
/**
|
|
71
|
+
* Save a JS object to a .synx file asynchronously.
|
|
72
|
+
*
|
|
73
|
+
* @param filePath - Path to the .synx file.
|
|
74
|
+
* @param obj - The object to serialize and save.
|
|
75
|
+
* @param active - If true, include `!active` directive.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* await Synx.save('config.synx', { app_name: 'TotalWario', port: 8080 });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
static save(filePath: string, obj: SynxObject, active?: boolean): Promise<void>;
|
|
57
83
|
/**
|
|
58
84
|
* Serialize a JS object back to .synx format string.
|
|
59
85
|
*
|
|
@@ -169,6 +195,70 @@ declare class Synx {
|
|
|
169
195
|
* ```
|
|
170
196
|
*/
|
|
171
197
|
static diff(a: SynxObject, b: SynxObject): SynxDiff;
|
|
198
|
+
/** Magic header for .synxb files */
|
|
199
|
+
private static readonly SYNXB_MAGIC;
|
|
200
|
+
/**
|
|
201
|
+
* Check if data is a `.synxb` binary file.
|
|
202
|
+
*
|
|
203
|
+
* @param data - Raw bytes to check.
|
|
204
|
+
* @returns True if the data starts with the SYNXB magic header.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* const buf = fs.readFileSync('config.synxb');
|
|
209
|
+
* if (Synx.isSynxb(buf)) { ... }
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
static isSynxb(data: Buffer | Uint8Array): boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Compile a .synx string into compact binary `.synxb` format.
|
|
215
|
+
*
|
|
216
|
+
* The binary format stores the parsed value tree with an interned string table.
|
|
217
|
+
* It is deterministic and much faster to load than re-parsing text.
|
|
218
|
+
*
|
|
219
|
+
* @param text - The .synx source text.
|
|
220
|
+
* @param resolved - If true and text is `!active`, resolve markers first.
|
|
221
|
+
* @returns A Uint8Array containing the `.synxb` binary.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* const binary = Synx.compile('name Alice\nage 30');
|
|
226
|
+
* fs.writeFileSync('config.synxb', binary);
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
static compile(text: string, resolved?: boolean): Uint8Array;
|
|
230
|
+
/**
|
|
231
|
+
* Decompile a `.synxb` binary back into a .synx text string.
|
|
232
|
+
*
|
|
233
|
+
* @param data - Raw `.synxb` bytes.
|
|
234
|
+
* @returns The reconstructed .synx text.
|
|
235
|
+
* @throws Error if the data is not valid `.synxb`.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* const buf = fs.readFileSync('config.synxb');
|
|
240
|
+
* const text = Synx.decompile(buf);
|
|
241
|
+
* console.log(text);
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
static decompile(data: Buffer | Uint8Array): string;
|
|
245
|
+
/**
|
|
246
|
+
* Parse a `!tool` mode SYNX text, reshaping into `{ tool, params }` format.
|
|
247
|
+
*
|
|
248
|
+
* @param text - The .synx text (should contain `!tool` directive).
|
|
249
|
+
* @param options - Optional settings.
|
|
250
|
+
* @returns `{ tool: string, params: SynxObject }`.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const toolCall = Synx.parseTool('!tool\ntool search\nparams\n query AI');
|
|
255
|
+
* // { tool: 'search', params: { query: 'AI' } }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
static parseTool(text: string, options?: SynxOptions): {
|
|
259
|
+
tool: string;
|
|
260
|
+
params: SynxObject;
|
|
261
|
+
};
|
|
172
262
|
}
|
|
173
263
|
type WatchCallback = (config: SynxObject, error?: Error) => void;
|
|
174
264
|
interface WatchHandle {
|
|
@@ -178,9 +268,10 @@ interface SynxSchemaProperty {
|
|
|
178
268
|
type?: string;
|
|
179
269
|
minimum?: number;
|
|
180
270
|
maximum?: number;
|
|
271
|
+
minLength?: number;
|
|
272
|
+
maxLength?: number;
|
|
181
273
|
pattern?: string;
|
|
182
274
|
enum?: string[];
|
|
183
|
-
required?: boolean;
|
|
184
275
|
}
|
|
185
276
|
interface SynxSchema {
|
|
186
277
|
$schema: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAe,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGzF,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAiEpC,cAAM,IAAI;IACR;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,CAAC;IAoC3F;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,CAAC;IAQlG;;;;;;;;;;;;OAYG;WACU,IAAI,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAQ7G;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,UAAQ,GAAG,MAAM;IAWzD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI;IAgBpE;;;;;;;OAOG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAWnE;;;;;;;;;;OAUG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAoBnE;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI;IAuBvE;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO;IAIzC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA4BnC,gEAAgE;IAChE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,UAAO,GAAG,MAAM;IAIrD,gEAAgE;IAChE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM;IAItC,gEAAgE;IAChE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM;IAItC,kFAAkF;IAClF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,SAAK,GAAG,MAAM;IAIlD,2FAA2F;IAC3F,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,WAAgB,GAAG,WAAW;IA0B/F,gEAAgE;IAChE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* SYNX — @aperturesyndicate/synx
|
|
3
|
+
* SYNX — @aperturesyndicate/synx-format
|
|
4
4
|
*
|
|
5
5
|
* The Active Data Format.
|
|
6
6
|
* Faster than JSON. Cheaper for AI tokens. Built-in logic.
|
|
@@ -189,6 +189,40 @@ class Synx {
|
|
|
189
189
|
const opts = options.basePath ? options : { ...options, basePath: path.dirname(absPath) };
|
|
190
190
|
return Synx.parse(text, opts);
|
|
191
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Save a JS object to a .synx file synchronously.
|
|
194
|
+
*
|
|
195
|
+
* @param filePath - Path to the .synx file.
|
|
196
|
+
* @param obj - The object to serialize and save.
|
|
197
|
+
* @param active - If true, include `!active` directive.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* Synx.saveSync('config.synx', { app_name: 'TotalWario', port: 8080 });
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
static saveSync(filePath, obj, active = false) {
|
|
205
|
+
const absPath = path.resolve(filePath);
|
|
206
|
+
const text = Synx.stringify(obj, active);
|
|
207
|
+
fs.writeFileSync(absPath, text, 'utf-8');
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Save a JS object to a .synx file asynchronously.
|
|
211
|
+
*
|
|
212
|
+
* @param filePath - Path to the .synx file.
|
|
213
|
+
* @param obj - The object to serialize and save.
|
|
214
|
+
* @param active - If true, include `!active` directive.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* await Synx.save('config.synx', { app_name: 'TotalWario', port: 8080 });
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
static async save(filePath, obj, active = false) {
|
|
222
|
+
const absPath = path.resolve(filePath);
|
|
223
|
+
const text = Synx.stringify(obj, active);
|
|
224
|
+
await fs.promises.writeFile(absPath, text, 'utf-8');
|
|
225
|
+
}
|
|
192
226
|
/**
|
|
193
227
|
* Serialize a JS object back to .synx format string.
|
|
194
228
|
*
|
|
@@ -447,7 +481,6 @@ class Synx {
|
|
|
447
481
|
if (c.enum)
|
|
448
482
|
prop.enum = c.enum;
|
|
449
483
|
if (c.required) {
|
|
450
|
-
prop.required = true;
|
|
451
484
|
required.push(key);
|
|
452
485
|
}
|
|
453
486
|
properties[key] = prop;
|
|
@@ -506,9 +539,296 @@ class Synx {
|
|
|
506
539
|
}
|
|
507
540
|
return { added, removed, changed, unchanged };
|
|
508
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* Check if data is a `.synxb` binary file.
|
|
544
|
+
*
|
|
545
|
+
* @param data - Raw bytes to check.
|
|
546
|
+
* @returns True if the data starts with the SYNXB magic header.
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```ts
|
|
550
|
+
* const buf = fs.readFileSync('config.synxb');
|
|
551
|
+
* if (Synx.isSynxb(buf)) { ... }
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
static isSynxb(data) {
|
|
555
|
+
if (data.length < 6)
|
|
556
|
+
return false;
|
|
557
|
+
for (let i = 0; i < 5; i++) {
|
|
558
|
+
if (data[i] !== Synx.SYNXB_MAGIC[i])
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Compile a .synx string into compact binary `.synxb` format.
|
|
565
|
+
*
|
|
566
|
+
* The binary format stores the parsed value tree with an interned string table.
|
|
567
|
+
* It is deterministic and much faster to load than re-parsing text.
|
|
568
|
+
*
|
|
569
|
+
* @param text - The .synx source text.
|
|
570
|
+
* @param resolved - If true and text is `!active`, resolve markers first.
|
|
571
|
+
* @returns A Uint8Array containing the `.synxb` binary.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* const binary = Synx.compile('name Alice\nage 30');
|
|
576
|
+
* fs.writeFileSync('config.synxb', binary);
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
static compile(text, resolved = false) {
|
|
580
|
+
const input = resolved && !/(?:^|\n)\s*!active\b/.test(text) ? '!active\n' + text : text;
|
|
581
|
+
const parsed = Synx.parse(input);
|
|
582
|
+
const strings = [];
|
|
583
|
+
const stringIndex = new Map();
|
|
584
|
+
function internString(s) {
|
|
585
|
+
if (stringIndex.has(s))
|
|
586
|
+
return stringIndex.get(s);
|
|
587
|
+
const idx = strings.length;
|
|
588
|
+
strings.push(s);
|
|
589
|
+
stringIndex.set(s, idx);
|
|
590
|
+
return idx;
|
|
591
|
+
}
|
|
592
|
+
// Collect all strings first
|
|
593
|
+
function collectStrings(val) {
|
|
594
|
+
if (val === null || val === undefined)
|
|
595
|
+
return;
|
|
596
|
+
if (typeof val === 'string') {
|
|
597
|
+
internString(val);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (Array.isArray(val)) {
|
|
601
|
+
val.forEach(collectStrings);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (typeof val === 'object') {
|
|
605
|
+
for (const [k, v] of Object.entries(val)) {
|
|
606
|
+
internString(k);
|
|
607
|
+
collectStrings(v);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
collectStrings(parsed);
|
|
612
|
+
// Encode
|
|
613
|
+
const buf = [];
|
|
614
|
+
// Magic + version
|
|
615
|
+
buf.push(0x53, 0x59, 0x4e, 0x58, 0x42); // SYNXB
|
|
616
|
+
buf.push(0x01); // version 1
|
|
617
|
+
// Flags: bit0=active, bit3=resolved
|
|
618
|
+
const isActive = text.trimStart().startsWith('!active');
|
|
619
|
+
let flags = 0;
|
|
620
|
+
if (isActive)
|
|
621
|
+
flags |= 0x01;
|
|
622
|
+
if (resolved)
|
|
623
|
+
flags |= 0x08;
|
|
624
|
+
buf.push(flags);
|
|
625
|
+
// String table
|
|
626
|
+
writeVarint(buf, strings.length);
|
|
627
|
+
for (const s of strings) {
|
|
628
|
+
const encoded = new TextEncoder().encode(s);
|
|
629
|
+
writeVarint(buf, encoded.length);
|
|
630
|
+
for (const b of encoded)
|
|
631
|
+
buf.push(b);
|
|
632
|
+
}
|
|
633
|
+
// Value tree
|
|
634
|
+
function writeValue(val) {
|
|
635
|
+
if (val === null || val === undefined) {
|
|
636
|
+
buf.push(0x00);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (typeof val === 'boolean') {
|
|
640
|
+
buf.push(val ? 0x02 : 0x01);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (typeof val === 'number') {
|
|
644
|
+
if (Number.isInteger(val)) {
|
|
645
|
+
buf.push(0x03);
|
|
646
|
+
writeZigzag(buf, val);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
buf.push(0x04);
|
|
650
|
+
const view = new DataView(new ArrayBuffer(8));
|
|
651
|
+
view.setFloat64(0, val, true); // little-endian
|
|
652
|
+
for (let i = 0; i < 8; i++)
|
|
653
|
+
buf.push(view.getUint8(i));
|
|
654
|
+
}
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (typeof val === 'string') {
|
|
658
|
+
buf.push(0x05);
|
|
659
|
+
writeVarint(buf, internString(val));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (Array.isArray(val)) {
|
|
663
|
+
buf.push(0x06);
|
|
664
|
+
writeVarint(buf, val.length);
|
|
665
|
+
val.forEach(writeValue);
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (typeof val === 'object') {
|
|
669
|
+
const entries = Object.entries(val);
|
|
670
|
+
buf.push(0x07);
|
|
671
|
+
writeVarint(buf, entries.length);
|
|
672
|
+
for (const [k, v] of entries) {
|
|
673
|
+
writeVarint(buf, internString(k));
|
|
674
|
+
writeValue(v);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
writeValue(parsed);
|
|
679
|
+
return new Uint8Array(buf);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Decompile a `.synxb` binary back into a .synx text string.
|
|
683
|
+
*
|
|
684
|
+
* @param data - Raw `.synxb` bytes.
|
|
685
|
+
* @returns The reconstructed .synx text.
|
|
686
|
+
* @throws Error if the data is not valid `.synxb`.
|
|
687
|
+
*
|
|
688
|
+
* @example
|
|
689
|
+
* ```ts
|
|
690
|
+
* const buf = fs.readFileSync('config.synxb');
|
|
691
|
+
* const text = Synx.decompile(buf);
|
|
692
|
+
* console.log(text);
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
static decompile(data) {
|
|
696
|
+
if (!Synx.isSynxb(data)) {
|
|
697
|
+
throw new types_1.SynxError('Not a valid .synxb file');
|
|
698
|
+
}
|
|
699
|
+
let offset = 5; // skip magic
|
|
700
|
+
const version = data[offset++];
|
|
701
|
+
if (version !== 1)
|
|
702
|
+
throw new types_1.SynxError(`Unsupported .synxb version: ${version}`);
|
|
703
|
+
const flags = data[offset++];
|
|
704
|
+
const isActive = (flags & 0x01) !== 0;
|
|
705
|
+
const isLocked = (flags & 0x02) !== 0;
|
|
706
|
+
// String table
|
|
707
|
+
const [strCount, o1] = readVarint(data, offset);
|
|
708
|
+
offset = o1;
|
|
709
|
+
const strings = [];
|
|
710
|
+
const decoder = new TextDecoder();
|
|
711
|
+
for (let i = 0; i < strCount; i++) {
|
|
712
|
+
const [len, o2] = readVarint(data, offset);
|
|
713
|
+
offset = o2;
|
|
714
|
+
strings.push(decoder.decode(data.slice(offset, offset + len)));
|
|
715
|
+
offset += len;
|
|
716
|
+
}
|
|
717
|
+
// Value tree
|
|
718
|
+
function readValue() {
|
|
719
|
+
const tag = data[offset++];
|
|
720
|
+
switch (tag) {
|
|
721
|
+
case 0x00: return null;
|
|
722
|
+
case 0x01: return false;
|
|
723
|
+
case 0x02: return true;
|
|
724
|
+
case 0x03: {
|
|
725
|
+
const [v, o] = readZigzag(data, offset);
|
|
726
|
+
offset = o;
|
|
727
|
+
return v;
|
|
728
|
+
}
|
|
729
|
+
case 0x04: {
|
|
730
|
+
const view = new DataView(data.buffer, data.byteOffset + offset, 8);
|
|
731
|
+
offset += 8;
|
|
732
|
+
return view.getFloat64(0, true);
|
|
733
|
+
}
|
|
734
|
+
case 0x05: {
|
|
735
|
+
const [idx, o] = readVarint(data, offset);
|
|
736
|
+
offset = o;
|
|
737
|
+
return strings[idx];
|
|
738
|
+
}
|
|
739
|
+
case 0x06: {
|
|
740
|
+
const [len, o] = readVarint(data, offset);
|
|
741
|
+
offset = o;
|
|
742
|
+
const arr = [];
|
|
743
|
+
for (let i = 0; i < len; i++)
|
|
744
|
+
arr.push(readValue());
|
|
745
|
+
return arr;
|
|
746
|
+
}
|
|
747
|
+
case 0x07: {
|
|
748
|
+
const [len, o] = readVarint(data, offset);
|
|
749
|
+
offset = o;
|
|
750
|
+
const obj = {};
|
|
751
|
+
for (let i = 0; i < len; i++) {
|
|
752
|
+
const [ki, o2] = readVarint(data, offset);
|
|
753
|
+
offset = o2;
|
|
754
|
+
obj[strings[ki]] = readValue();
|
|
755
|
+
}
|
|
756
|
+
return obj;
|
|
757
|
+
}
|
|
758
|
+
case 0x08: {
|
|
759
|
+
const [idx, o] = readVarint(data, offset);
|
|
760
|
+
offset = o;
|
|
761
|
+
return '[SECRET]';
|
|
762
|
+
}
|
|
763
|
+
default: throw new types_1.SynxError(`Unknown value tag: 0x${tag.toString(16)}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const value = readValue();
|
|
767
|
+
let header = '';
|
|
768
|
+
if (isActive)
|
|
769
|
+
header += '!active\n';
|
|
770
|
+
if (isLocked)
|
|
771
|
+
header += '!lock\n';
|
|
772
|
+
if (header)
|
|
773
|
+
header += '\n';
|
|
774
|
+
return header + serializeObject(value, 0);
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Parse a `!tool` mode SYNX text, reshaping into `{ tool, params }` format.
|
|
778
|
+
*
|
|
779
|
+
* @param text - The .synx text (should contain `!tool` directive).
|
|
780
|
+
* @param options - Optional settings.
|
|
781
|
+
* @returns `{ tool: string, params: SynxObject }`.
|
|
782
|
+
*
|
|
783
|
+
* @example
|
|
784
|
+
* ```ts
|
|
785
|
+
* const toolCall = Synx.parseTool('!tool\ntool search\nparams\n query AI');
|
|
786
|
+
* // { tool: 'search', params: { query: 'AI' } }
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
static parseTool(text, options = {}) {
|
|
790
|
+
const parsed = Synx.parse(text, options);
|
|
791
|
+
const tool = typeof parsed.tool === 'string' ? parsed.tool : '';
|
|
792
|
+
const params = (typeof parsed.params === 'object' && parsed.params !== null && !Array.isArray(parsed.params))
|
|
793
|
+
? parsed.params
|
|
794
|
+
: {};
|
|
795
|
+
return { tool, params };
|
|
796
|
+
}
|
|
509
797
|
}
|
|
510
798
|
exports.Synx = Synx;
|
|
511
|
-
// ───
|
|
799
|
+
// ─── Binary Format (.synxb) ─────────────────────────────
|
|
800
|
+
/** Magic header for .synxb files */
|
|
801
|
+
Synx.SYNXB_MAGIC = new Uint8Array([0x53, 0x59, 0x4e, 0x58, 0x42]); // "SYNXB"
|
|
802
|
+
// ─── Binary encoding helpers ──────────────────────────────
|
|
803
|
+
function writeVarint(buf, value) {
|
|
804
|
+
let v = value >>> 0;
|
|
805
|
+
while (v > 0x7f) {
|
|
806
|
+
buf.push((v & 0x7f) | 0x80);
|
|
807
|
+
v >>>= 7;
|
|
808
|
+
}
|
|
809
|
+
buf.push(v);
|
|
810
|
+
}
|
|
811
|
+
function readVarint(data, offset) {
|
|
812
|
+
let result = 0;
|
|
813
|
+
let shift = 0;
|
|
814
|
+
while (offset < data.length) {
|
|
815
|
+
const byte = data[offset++];
|
|
816
|
+
result |= (byte & 0x7f) << shift;
|
|
817
|
+
if ((byte & 0x80) === 0)
|
|
818
|
+
break;
|
|
819
|
+
shift += 7;
|
|
820
|
+
}
|
|
821
|
+
return [result >>> 0, offset];
|
|
822
|
+
}
|
|
823
|
+
function writeZigzag(buf, value) {
|
|
824
|
+
const zigzag = (value << 1) ^ (value >> 31);
|
|
825
|
+
writeVarint(buf, zigzag >>> 0);
|
|
826
|
+
}
|
|
827
|
+
function readZigzag(data, offset) {
|
|
828
|
+
const [raw, newOffset] = readVarint(data, offset);
|
|
829
|
+
const value = (raw >>> 1) ^ -(raw & 1);
|
|
830
|
+
return [value, newOffset];
|
|
831
|
+
}
|
|
512
832
|
function deepEqual(a, b) {
|
|
513
833
|
if (a === b)
|
|
514
834
|
return true;
|