xcdn 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gioele Stefano Luca Fierro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # > xCDN_ (JavaScript)
2
+
3
+ A complete JavaScript library to parse, serialize and deserialize **> xCDN_ — eXtensible Cognitive Data Notation**.
4
+
5
+ > **What is > xCDN_?**
6
+ > xCDN_ is a human-first, machine-optimized data notation with native types, tags and annotations.
7
+ > It supports comments, trailing commas, unquoted keys and multi-line strings.
8
+ > You can read more about this notation in the [> xCDN_ repository](https://github.com/gslf/xCDN).
9
+
10
+ ## Features
11
+
12
+ - Full streaming document model (one or more top-level values)
13
+ - Optional **prolog** (`$schema: "..."`, ...)
14
+ - Objects, arrays and scalars
15
+ - Native types: `Decimal` (`d"..."`), `UUID` (`u"..."`), `DateTime` (`t"..."` RFC3339),
16
+ `Duration` (`r"..."` ISO8601), `Bytes` (`b"..."` Base64)
17
+ - `#tags` and `@annotations(args?)` that decorate any value
18
+ - Comments: `//` and `/* ... */`
19
+ - Trailing commas and unquoted keys
20
+ - Pretty or compact serialization
21
+ - Zero dependencies
22
+
23
+ ## Example
24
+
25
+ ```xcdn
26
+ $schema: "https://gslf.github.io/xCDN/schemas/v1/meta.xcdn",
27
+
28
+ server_config: {
29
+ host: "localhost",
30
+ // Unquoted keys & trailing commas? Yes.
31
+ ports: [8080, 9090,],
32
+
33
+ // Native Decimals & ISO8601 Duration
34
+ timeout: r"PT30S",
35
+ max_cost: d"19.99",
36
+
37
+ // Semantic Tagging
38
+ admin: #user {
39
+ id: u"550e8400-e29b-41d4-a716-446655440000",
40
+ role: "superuser"
41
+ },
42
+
43
+ // Binary data handling
44
+ icon: @mime("image/png") b"iVBORw0KGgoAAAANSUhEUgAAAAUA...",
45
+ }
46
+ ```
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ npm install xcdn
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ ```javascript
57
+ import xcdn from 'xcdn';
58
+
59
+ // Parse an xCDN string
60
+ const doc = xcdn.parse(`
61
+ name: "Alice",
62
+ age: 30,
63
+ active: true,
64
+ `);
65
+
66
+ // Pretty-print
67
+ console.log(xcdn.stringify(doc));
68
+
69
+ // Compact output
70
+ console.log(xcdn.stringifyCompact(doc));
71
+ ```
72
+
73
+ ### Named imports
74
+
75
+ ```javascript
76
+ import { parseStr, toStringPretty, toStringCompact } from 'xcdn';
77
+
78
+ const doc = parseStr(source);
79
+ const pretty = toStringPretty(doc);
80
+ const compact = toStringCompact(doc);
81
+ ```
82
+
83
+ ### Accessing fields
84
+
85
+ ```javascript
86
+ import { parseStr } from 'xcdn';
87
+
88
+ const doc = parseStr(`
89
+ user: {
90
+ name: "Mario Rossi",
91
+ age: 30,
92
+ roles: [#admin "administrator", #user "standard"],
93
+ },
94
+ session_id: u"550e8400-e29b-41d4-a716-446655440000",
95
+ balance: d"1234.56",
96
+ created_at: t"2025-01-15T10:30:00Z",
97
+ `);
98
+
99
+ const root = doc.values[0].value;
100
+
101
+ // Access nested values
102
+ root.get('user').value.get('name').value.value; // "Mario Rossi"
103
+
104
+ // Check keys
105
+ root.has('user'); // true
106
+ root.keys(); // ["user", "session_id", "balance", "created_at"]
107
+
108
+ // Iterate decorated items
109
+ const roles = root.get('user').value.get('roles').value;
110
+ for (const role of roles) {
111
+ console.log(role.tags[0].name, role.value.value);
112
+ // "admin" "administrator"
113
+ // "user" "standard"
114
+ }
115
+ ```
116
+
117
+ ### Sub-module imports
118
+
119
+ ```javascript
120
+ import { Parser } from 'xcdn/parser';
121
+ import { Serializer, Format } from 'xcdn/serializer';
122
+ import { XObject, XArray, Node, XString } from 'xcdn/ast';
123
+ import { XCDNError } from 'xcdn/error';
124
+ import { Lexer } from 'xcdn/lexer';
125
+ ```
126
+
127
+ ## Testing
128
+
129
+ ```bash
130
+ npm test
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT, see [LICENSE](LICENSE).
136
+
137
+ ---
138
+
139
+ #### This is a :/# GSLF project.
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "xcdn",
3
+ "version": "1.0.0",
4
+ "description": "xCDN parser and serializer for JavaScript",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./ast": "./src/ast.js",
10
+ "./error": "./src/error.js",
11
+ "./lexer": "./src/lexer.js",
12
+ "./parser": "./src/parser.js",
13
+ "./serializer": "./src/serializer.js"
14
+ },
15
+ "scripts": {
16
+ "test": "node --test tests/"
17
+ },
18
+ "keywords": [
19
+ "xcdn",
20
+ "parser",
21
+ "serializer",
22
+ "data-format"
23
+ ],
24
+ "author": "GioeleSLFierro",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/gslf/xCDN-JavaScript.git"
28
+ },
29
+ "files": [
30
+ "src/",
31
+ "LICENSE",
32
+ "README.md"
33
+ ],
34
+ "license": "MIT"
35
+ }
package/src/ast.js ADDED
@@ -0,0 +1,409 @@
1
+ /**
2
+ * xCDN AST Module
3
+ * Data structures for the xCDN format
4
+ */
5
+
6
+ /**
7
+ * Main xCDN document
8
+ */
9
+ export class Document {
10
+ /**
11
+ * @param {Directive[]} prolog - Initial directives ($schema, etc.)
12
+ * @param {Node[]} values - Top-level values
13
+ */
14
+ constructor(prolog = [], values = []) {
15
+ this.prolog = prolog;
16
+ this.values = values;
17
+ }
18
+
19
+ /**
20
+ * Dict-like access
21
+ * @param {number|string} key
22
+ */
23
+ get(key) {
24
+ if (typeof key === 'number') {
25
+ return this.values[key];
26
+ }
27
+ // Access the first object
28
+ if (this.values.length > 0 && this.values[0].value instanceof XObject) {
29
+ return this.values[0].value.get(key);
30
+ }
31
+ return undefined;
32
+ }
33
+
34
+ /**
35
+ * Set dict-like
36
+ * @param {string} key
37
+ * @param {*} value
38
+ */
39
+ set(key, value) {
40
+ if (this.values.length > 0 && this.values[0].value instanceof XObject) {
41
+ this.values[0].value.set(key, value);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Check if key exists
47
+ * @param {string} key
48
+ */
49
+ has(key) {
50
+ if (this.values.length > 0 && this.values[0].value instanceof XObject) {
51
+ return this.values[0].value.has(key);
52
+ }
53
+ return false;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Directive ($name: value)
59
+ */
60
+ export class Directive {
61
+ /**
62
+ * @param {string} name - Directive name (without $)
63
+ * @param {*} value - Directive value
64
+ */
65
+ constructor(name, value) {
66
+ this.name = name;
67
+ this.value = value;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Node decorated with tags and annotations
73
+ */
74
+ export class Node {
75
+ /**
76
+ * @param {Tag[]} tags - List of tags (#tag)
77
+ * @param {Annotation[]} annotations - List of annotations (@anno)
78
+ * @param {*} value - Actual value
79
+ */
80
+ constructor(tags = [], annotations = [], value = null) {
81
+ this.tags = tags;
82
+ this.annotations = annotations;
83
+ this.value = value;
84
+ }
85
+
86
+ // Dict-like methods delegated to the value
87
+ get(key, defaultValue = undefined) {
88
+ if (this.value && typeof this.value.get === 'function') {
89
+ return this.value.get(key, defaultValue);
90
+ }
91
+ return defaultValue;
92
+ }
93
+
94
+ set(key, value) {
95
+ if (this.value && typeof this.value.set === 'function') {
96
+ this.value.set(key, value);
97
+ }
98
+ }
99
+
100
+ has(key) {
101
+ if (this.value && typeof this.value.has === 'function') {
102
+ return this.value.has(key);
103
+ }
104
+ return false;
105
+ }
106
+
107
+ keys() {
108
+ if (this.value instanceof XObject) {
109
+ return this.value.keys();
110
+ }
111
+ return [];
112
+ }
113
+
114
+ valuesIter() {
115
+ if (this.value instanceof XObject) {
116
+ return this.value.valuesIter();
117
+ }
118
+ return [];
119
+ }
120
+
121
+ items() {
122
+ if (this.value instanceof XObject) {
123
+ return this.value.items();
124
+ }
125
+ return [];
126
+ }
127
+
128
+ append(value) {
129
+ if (this.value instanceof XArray) {
130
+ this.value.append(value);
131
+ }
132
+ }
133
+
134
+ get length() {
135
+ if (this.value && typeof this.value.length !== 'undefined') {
136
+ return this.value.length;
137
+ }
138
+ return 0;
139
+ }
140
+
141
+ [Symbol.iterator]() {
142
+ if (this.value && typeof this.value[Symbol.iterator] === 'function') {
143
+ return this.value[Symbol.iterator]();
144
+ }
145
+ return [][Symbol.iterator]();
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Tag (#tag)
151
+ */
152
+ export class Tag {
153
+ /**
154
+ * @param {string} name - Tag name
155
+ */
156
+ constructor(name) {
157
+ this.name = name;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Annotation (@name(args...))
163
+ */
164
+ export class Annotation {
165
+ /**
166
+ * @param {string} name - Annotation name
167
+ * @param {*[]} args - Optional arguments
168
+ */
169
+ constructor(name, args = []) {
170
+ this.name = name;
171
+ this.args = args;
172
+ }
173
+ }
174
+
175
+ // ============== VALUE TYPES ==============
176
+
177
+ /**
178
+ * Base class for value types
179
+ */
180
+ export class ValueType {
181
+ constructor() {
182
+ if (new.target === ValueType) {
183
+ throw new Error('ValueType is abstract');
184
+ }
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Null value
190
+ */
191
+ export class Null extends ValueType {
192
+ constructor() {
193
+ super();
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Boolean value
199
+ */
200
+ export class Bool extends ValueType {
201
+ /**
202
+ * @param {boolean} value
203
+ */
204
+ constructor(value) {
205
+ super();
206
+ this.value = value;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Integer value
212
+ */
213
+ export class Int extends ValueType {
214
+ /**
215
+ * @param {number|bigint} value
216
+ */
217
+ constructor(value) {
218
+ super();
219
+ this.value = typeof value === 'bigint' ? value : BigInt(Math.trunc(value));
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Float value
225
+ */
226
+ export class Float extends ValueType {
227
+ /**
228
+ * @param {number} value
229
+ */
230
+ constructor(value) {
231
+ super();
232
+ this.value = value;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Decimal value (d"...")
238
+ */
239
+ export class DecimalValue extends ValueType {
240
+ /**
241
+ * @param {string} value - String representation of the decimal
242
+ */
243
+ constructor(value) {
244
+ super();
245
+ this.value = value; // Keep as string for precision
246
+ }
247
+ }
248
+
249
+ /**
250
+ * String value
251
+ */
252
+ export class XString extends ValueType {
253
+ /**
254
+ * @param {string} value
255
+ */
256
+ constructor(value) {
257
+ super();
258
+ this.value = value;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Bytes value (b"..." base64)
264
+ */
265
+ export class Bytes extends ValueType {
266
+ /**
267
+ * @param {Uint8Array} value - Decoded bytes
268
+ */
269
+ constructor(value) {
270
+ super();
271
+ this.value = value;
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Datetime value (t"...")
277
+ */
278
+ export class DateTime extends ValueType {
279
+ /**
280
+ * @param {Date} value
281
+ */
282
+ constructor(value) {
283
+ super();
284
+ this.value = value;
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Duration value (r"...")
290
+ */
291
+ export class Duration extends ValueType {
292
+ /**
293
+ * @param {string} value - ISO8601 duration as string
294
+ */
295
+ constructor(value) {
296
+ super();
297
+ this.value = value;
298
+ }
299
+ }
300
+
301
+ /**
302
+ * UUID value (u"...")
303
+ */
304
+ export class Uuid extends ValueType {
305
+ /**
306
+ * @param {string} value - UUID as string
307
+ */
308
+ constructor(value) {
309
+ super();
310
+ this.value = value;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * xCDN Array
316
+ */
317
+ export class XArray extends ValueType {
318
+ /**
319
+ * @param {Node[]} value - List of nodes
320
+ */
321
+ constructor(value = []) {
322
+ super();
323
+ this.value = value;
324
+ }
325
+
326
+ get(index) {
327
+ return this.value[index];
328
+ }
329
+
330
+ set(index, val) {
331
+ this.value[index] = val;
332
+ }
333
+
334
+ get length() {
335
+ return this.value.length;
336
+ }
337
+
338
+ append(val) {
339
+ // Wrap in Node if not already
340
+ if (val instanceof Node) {
341
+ this.value.push(val);
342
+ } else {
343
+ this.value.push(new Node([], [], val));
344
+ }
345
+ }
346
+
347
+ [Symbol.iterator]() {
348
+ return this.value[Symbol.iterator]();
349
+ }
350
+ }
351
+
352
+ /**
353
+ * xCDN Object
354
+ */
355
+ export class XObject extends ValueType {
356
+ /**
357
+ * @param {Map<string, Node>|Object} value - Key-value map
358
+ */
359
+ constructor(value = new Map()) {
360
+ super();
361
+ if (value instanceof Map) {
362
+ this.value = value;
363
+ } else {
364
+ this.value = new Map(Object.entries(value));
365
+ }
366
+ }
367
+
368
+ get(key, defaultValue = undefined) {
369
+ return this.value.has(key) ? this.value.get(key) : defaultValue;
370
+ }
371
+
372
+ set(key, val) {
373
+ // Wrap in Node if not already
374
+ if (val instanceof Node) {
375
+ this.value.set(key, val);
376
+ } else {
377
+ this.value.set(key, new Node([], [], val));
378
+ }
379
+ }
380
+
381
+ has(key) {
382
+ return this.value.has(key);
383
+ }
384
+
385
+ get length() {
386
+ return this.value.size;
387
+ }
388
+
389
+ keys() {
390
+ return Array.from(this.value.keys());
391
+ }
392
+
393
+ valuesIter() {
394
+ return Array.from(this.value.values());
395
+ }
396
+
397
+ items() {
398
+ return Array.from(this.value.entries());
399
+ }
400
+
401
+ [Symbol.iterator]() {
402
+ return this.value.keys()[Symbol.iterator]();
403
+ }
404
+ }
405
+
406
+ // Export aliases for compatibility
407
+ export { XString as String };
408
+ export { XArray as Array };
409
+ export { XObject as Object };
package/src/error.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * xCDN Error Module
3
+ * Error handling for the xCDN parser
4
+ */
5
+
6
+ /**
7
+ * Represents a position in the source code
8
+ */
9
+ export class Span {
10
+ /**
11
+ * @param {number} offset - Absolute byte offset
12
+ * @param {number} line - Line number (1-indexed)
13
+ * @param {number} column - Column number (1-indexed)
14
+ */
15
+ constructor(offset, line, column) {
16
+ this.offset = offset;
17
+ this.line = line;
18
+ this.column = column;
19
+ }
20
+
21
+ toString() {
22
+ return `${this.line}:${this.column}`;
23
+ }
24
+
25
+ clone() {
26
+ return new Span(this.offset, this.line, this.column);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Possible error types
32
+ */
33
+ export const ErrorKind = {
34
+ Eof: 'Eof',
35
+ InvalidToken: 'InvalidToken',
36
+ Expected: 'Expected',
37
+ InvalidEscape: 'InvalidEscape',
38
+ InvalidNumber: 'InvalidNumber',
39
+ InvalidDecimal: 'InvalidDecimal',
40
+ InvalidDateTime: 'InvalidDateTime',
41
+ InvalidDuration: 'InvalidDuration',
42
+ InvalidUuid: 'InvalidUuid',
43
+ InvalidBase64: 'InvalidBase64',
44
+ Message: 'Message',
45
+ };
46
+
47
+ /**
48
+ * xCDN error with position information
49
+ */
50
+ export class XCDNError extends Error {
51
+ /**
52
+ * @param {string} kind - Error type (from ErrorKind)
53
+ * @param {Span} span - Error position
54
+ * @param {string} [context] - Additional context
55
+ */
56
+ constructor(kind, span, context = null) {
57
+ const message = XCDNError.formatMessage(kind, context, span);
58
+ super(message);
59
+ this.name = 'XCDNError';
60
+ this.kind = kind;
61
+ this.span = span;
62
+ this.context = context;
63
+ }
64
+
65
+ static formatMessage(kind, context, span) {
66
+ let msg;
67
+ switch (kind) {
68
+ case ErrorKind.Eof:
69
+ msg = 'Unexpected end of input';
70
+ break;
71
+ case ErrorKind.InvalidToken:
72
+ msg = `Invalid token: ${context}`;
73
+ break;
74
+ case ErrorKind.Expected:
75
+ msg = context; // "Expected X, found Y"
76
+ break;
77
+ case ErrorKind.InvalidEscape:
78
+ msg = 'Invalid escape sequence';
79
+ break;
80
+ case ErrorKind.InvalidNumber:
81
+ msg = 'Invalid number format';
82
+ break;
83
+ case ErrorKind.InvalidDecimal:
84
+ msg = `Invalid decimal value: ${context}`;
85
+ break;
86
+ case ErrorKind.InvalidDateTime:
87
+ msg = `Invalid datetime value: ${context}`;
88
+ break;
89
+ case ErrorKind.InvalidDuration:
90
+ msg = `Invalid duration value: ${context}`;
91
+ break;
92
+ case ErrorKind.InvalidUuid:
93
+ msg = `Invalid UUID value: ${context}`;
94
+ break;
95
+ case ErrorKind.InvalidBase64:
96
+ msg = `Invalid base64 value: ${context}`;
97
+ break;
98
+ case ErrorKind.Message:
99
+ msg = context;
100
+ break;
101
+ default:
102
+ msg = context || 'Unknown error';
103
+ }
104
+ return `${msg} at ${span.toString()}`;
105
+ }
106
+ }