yamlstar 0.0.1 → 0.1.2

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/Makefile CHANGED
@@ -1,21 +1,24 @@
1
- SHELL := bash
1
+ include ../common/init.mk
2
+ include $M/node.mk
3
+ include $(COMMON)/common.mk
4
+ include $(COMMON)/binding.mk
2
5
 
3
- COFFEE := $(shell find src -type f -name '*.coffee')
4
- JS := $(COFFEE:%.coffee=%.js)
5
- JS := $(JS:src/%=lib/%)
6
+ NODE-MODULES := node_modules
6
7
 
7
- default:
8
- @printf '%s\n' $(JS)
8
+ MAKES-CLEAN := \
9
+ .npmrc \
10
+ package-lock.json \
9
11
 
10
- build: $(JS)
12
+ MAKES-REALCLEAN := \
13
+ $(NODE-MODULES) \
11
14
 
12
- lib/%.js: src/%.coffee
13
- @mkdir -p $(dir $@)
14
- coffee -c -p $< > $@
15
15
 
16
- publish: build
17
- npm $@
16
+ test: $(NODE-MODULES) $(LIBYAMLSTAR-SO)
17
+ node test/test.js
18
18
 
19
- clean:
20
- rm -f package*
21
- rm -fr node_modules
19
+ build: $(LIBYAMLSTAR-SO)
20
+
21
+ node_modules: package.json $(NODE)
22
+ npm install
23
+
24
+ release-deps:: $(NODE)
package/ReadMe.md ADDED
@@ -0,0 +1,326 @@
1
+ # YAMLStar Node.js Bindings
2
+
3
+ Node.js bindings for YAMLStar - a pure YAML 1.2 loader implemented in Clojure.
4
+
5
+ ## Features
6
+
7
+ - **YAML 1.2 Spec Compliance**: 100% compliant with YAML 1.2 core schema
8
+ - **Pure Implementation**: No dependencies on js-yaml or other external parsers
9
+ - **Fast Native Performance**: Uses GraalVM native-image shared library
10
+ - **Simple API**: Load YAML documents with a single method call
11
+ - **Multi-Document Support**: Load multiple YAML documents from a single string
12
+
13
+ ## Installation
14
+
15
+ ### Prerequisites
16
+
17
+ First, build and install the shared library:
18
+
19
+ ```bash
20
+ cd ../libyamlstar
21
+ make native
22
+ sudo make install PREFIX=/usr/local
23
+ ```
24
+
25
+ Or install to user-local directory:
26
+
27
+ ```bash
28
+ cd ../libyamlstar
29
+ make native
30
+ make install PREFIX=~/.local
31
+ ```
32
+
33
+ ### Install Node.js Package
34
+
35
+ ```bash
36
+ npm install @yaml/yamlstar
37
+ ```
38
+
39
+ For development:
40
+
41
+ ```bash
42
+ npm install
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```javascript
48
+ const YAMLStar = require('@yaml/yamlstar');
49
+
50
+ // Create a YAMLStar instance
51
+ const ys = new YAMLStar();
52
+
53
+ // Load a simple YAML string
54
+ const data = ys.load('key: value');
55
+ console.log(data); // { key: 'value' }
56
+
57
+ // Clean up when done
58
+ ys.close();
59
+ ```
60
+
61
+ ## Usage Examples
62
+
63
+ ### Basic Types
64
+
65
+ ```javascript
66
+ const YAMLStar = require('@yaml/yamlstar');
67
+ const ys = new YAMLStar();
68
+
69
+ // Strings
70
+ ys.load('hello'); // 'hello'
71
+
72
+ // Integers
73
+ ys.load('42'); // 42
74
+
75
+ // Floats
76
+ ys.load('3.14'); // 3.14
77
+
78
+ // Booleans
79
+ ys.load('true'); // true
80
+ ys.load('false'); // false
81
+
82
+ // Null
83
+ ys.load('null'); // null
84
+ ```
85
+
86
+ ### Collections
87
+
88
+ ```javascript
89
+ // Mappings (objects)
90
+ const data = ys.load(`
91
+ name: Alice
92
+ age: 30
93
+ city: Seattle
94
+ `);
95
+ // { name: 'Alice', age: 30, city: 'Seattle' }
96
+
97
+ // Sequences (arrays)
98
+ const data = ys.load(`
99
+ - apple
100
+ - banana
101
+ - orange
102
+ `);
103
+ // ['apple', 'banana', 'orange']
104
+
105
+ // Flow style
106
+ const data = ys.load('[a, b, c]');
107
+ // ['a', 'b', 'c']
108
+ ```
109
+
110
+ ### Nested Structures
111
+
112
+ ```javascript
113
+ const data = ys.load(`
114
+ person:
115
+ name: Alice
116
+ age: 30
117
+ hobbies:
118
+ - reading
119
+ - coding
120
+ - hiking
121
+ `);
122
+ // {
123
+ // person: {
124
+ // name: 'Alice',
125
+ // age: 30,
126
+ // hobbies: ['reading', 'coding', 'hiking']
127
+ // }
128
+ // }
129
+ ```
130
+
131
+ ### Multi-Document YAML
132
+
133
+ ```javascript
134
+ // Load all documents from a multi-document YAML string
135
+ const docs = ys.loadAll(`---
136
+ name: Document 1
137
+ ---
138
+ name: Document 2
139
+ ---
140
+ name: Document 3
141
+ `);
142
+ // [
143
+ // { name: 'Document 1' },
144
+ // { name: 'Document 2' },
145
+ // { name: 'Document 3' }
146
+ // ]
147
+ ```
148
+
149
+ ### Type Coercion
150
+
151
+ YAMLStar follows YAML 1.2 core schema type inference:
152
+
153
+ ```javascript
154
+ const data = ys.load(`
155
+ string: hello
156
+ integer: 42
157
+ float: 3.14
158
+ bool_true: true
159
+ bool_false: false
160
+ null_value: null
161
+ `);
162
+ // {
163
+ // string: 'hello',
164
+ // integer: 42,
165
+ // float: 3.14,
166
+ // bool_true: true,
167
+ // bool_false: false,
168
+ // null_value: null
169
+ // }
170
+ ```
171
+
172
+ ### Error Handling
173
+
174
+ ```javascript
175
+ try {
176
+ const data = ys.load('invalid: yaml: syntax');
177
+ } catch (error) {
178
+ console.error('Error loading YAML:', error.message);
179
+ }
180
+ ```
181
+
182
+ ### Version Information
183
+
184
+ ```javascript
185
+ // Get YAMLStar version
186
+ const version = ys.version();
187
+ console.log(`YAMLStar version: ${version}`);
188
+ ```
189
+
190
+ ### Resource Cleanup
191
+
192
+ ```javascript
193
+ const ys = new YAMLStar();
194
+
195
+ // ... use ys ...
196
+
197
+ // Clean up GraalVM isolate when done
198
+ ys.close();
199
+ ```
200
+
201
+ ## API Reference
202
+
203
+ ### `YAMLStar` Class
204
+
205
+ #### `new YAMLStar()`
206
+ Create a new YAMLStar instance. Each instance maintains its own GraalVM isolate.
207
+
208
+ ```javascript
209
+ const ys = new YAMLStar();
210
+ ```
211
+
212
+ #### `load(yamlInput)`
213
+ Load a single YAML document.
214
+
215
+ **Parameters:**
216
+ - `yamlInput` (string): String containing YAML content
217
+
218
+ **Returns:**
219
+ - JavaScript value representing the YAML document (object, array, string, number, boolean, or null)
220
+
221
+ **Throws:**
222
+ - `Error` if the YAML is malformed
223
+
224
+ **Example:**
225
+ ```javascript
226
+ const data = ys.load('key: value');
227
+ ```
228
+
229
+ #### `loadAll(yamlInput)`
230
+ Load all YAML documents from a multi-document string.
231
+
232
+ **Parameters:**
233
+ - `yamlInput` (string): String containing one or more YAML documents
234
+
235
+ **Returns:**
236
+ - Array of JavaScript values, one per YAML document
237
+
238
+ **Throws:**
239
+ - `Error` if the YAML is malformed
240
+
241
+ **Example:**
242
+ ```javascript
243
+ const docs = ys.loadAll('---\ndoc1\n---\ndoc2');
244
+ ```
245
+
246
+ #### `version()`
247
+ Get the YAMLStar version string.
248
+
249
+ **Returns:**
250
+ - string: Version string
251
+
252
+ **Example:**
253
+ ```javascript
254
+ const version = ys.version();
255
+ ```
256
+
257
+ #### `close()`
258
+ Tear down the GraalVM isolate and free resources. Should be called when done using the instance.
259
+
260
+ **Example:**
261
+ ```javascript
262
+ ys.close();
263
+ ```
264
+
265
+ ## Development
266
+
267
+ ### Running Tests
268
+
269
+ ```bash
270
+ # Run all tests
271
+ make test
272
+
273
+ # Or directly with node
274
+ node test/test.js
275
+ ```
276
+
277
+ ### Building libyamlstar
278
+
279
+ If you need to rebuild the shared library:
280
+
281
+ ```bash
282
+ cd ../libyamlstar
283
+ make clean
284
+ make native
285
+ ```
286
+
287
+ ## Requirements
288
+
289
+ - **Node.js**: 18.0.0 or higher
290
+ - **libyamlstar**: Shared library (installed separately)
291
+ - **System**: Linux or macOS
292
+ - **Dependencies**: `@makeomatic/ffi-napi`, `ref-napi`
293
+
294
+ ## Library Search Path
295
+
296
+ The module searches for `libyamlstar.so` (or `.dylib` on macOS) in:
297
+
298
+ 1. Development path (relative to module: `../../libyamlstar/lib/`)
299
+ 2. Directories in `LD_LIBRARY_PATH` environment variable
300
+ 3. `/usr/local/lib` (default install location)
301
+ 4. `~/.local/lib` (user-local install location)
302
+
303
+ ## Comparison to js-yaml
304
+
305
+ | Feature | YAMLStar | js-yaml |
306
+ |---------|----------|---------|
307
+ | YAML Version | 1.2 | 1.2 |
308
+ | Implementation | Pure Clojure | JavaScript |
309
+ | Type Inference | YAML 1.2 core schema | YAML 1.2 + custom |
310
+ | Native Performance | Yes (GraalVM) | No |
311
+ | Dependencies | libyamlstar.so | None |
312
+
313
+ ## License
314
+
315
+ MIT License - See [License](License) file
316
+
317
+ ## Credits
318
+
319
+ Created by Ingy döt Net, inventor of YAML.
320
+
321
+ YAMLStar is built on the YAML Reference Parser (pure Clojure implementation).
322
+
323
+ ## Links
324
+
325
+ - **GitHub**: https://github.com/yaml/yamlstar
326
+ - **YAML Specification**: https://yaml.org/spec/1.2/spec.html
@@ -0,0 +1,125 @@
1
+ const yamlstarVersion = '0.1.2';
2
+
3
+ const ffi = require('@makeomatic/ffi-napi');
4
+ const ref = require('ref-napi');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const os = require('os');
8
+
9
+ function defineForeignFunctionInterface() {
10
+ let libPath = findLibyamlstarPath();
11
+
12
+ return ffi.Library(libPath, {
13
+ 'graal_create_isolate': ['int', ['pointer', 'pointer', 'pointer']],
14
+ 'graal_tear_down_isolate': ['int', ['pointer']],
15
+ 'yamlstar_load': ['string', ['pointer', 'string']],
16
+ 'yamlstar_load_all': ['string', ['pointer', 'string']],
17
+ 'yamlstar_version': ['string', ['pointer']],
18
+ });
19
+ }
20
+
21
+ class YAMLStar {
22
+ constructor(config = {}) {
23
+ this.libyamlstar = defineForeignFunctionInterface();
24
+ this.isolatethread = ref.NULL_POINTER;
25
+
26
+ let rc = this.libyamlstar.graal_create_isolate(
27
+ null,
28
+ null,
29
+ this.isolatethread,
30
+ );
31
+
32
+ if (rc !== 0) {
33
+ throw new Error('Failed to create GraalVM isolate');
34
+ }
35
+ }
36
+
37
+ load(input) {
38
+ let dataJson = this.libyamlstar.yamlstar_load(
39
+ this.isolatethread.deref(),
40
+ input,
41
+ );
42
+
43
+ let resp = JSON.parse(dataJson);
44
+
45
+ if (resp.error) {
46
+ throw new Error(`libyamlstar: ${resp.error.cause}`);
47
+ }
48
+
49
+ if (!('data' in resp)) {
50
+ throw new Error("Unexpected response from 'libyamlstar'");
51
+ }
52
+
53
+ return resp.data;
54
+ }
55
+
56
+ loadAll(input) {
57
+ let dataJson = this.libyamlstar.yamlstar_load_all(
58
+ this.isolatethread.deref(),
59
+ input,
60
+ );
61
+
62
+ let resp = JSON.parse(dataJson);
63
+
64
+ if (resp.error) {
65
+ throw new Error(`libyamlstar: ${resp.error.cause}`);
66
+ }
67
+
68
+ if (!('data' in resp)) {
69
+ throw new Error("Unexpected response from 'libyamlstar'");
70
+ }
71
+
72
+ return resp.data;
73
+ }
74
+
75
+ version() {
76
+ return this.libyamlstar.yamlstar_version(this.isolatethread.deref());
77
+ }
78
+
79
+ close() {
80
+ let ret = this.libyamlstar.graal_tear_down_isolate(
81
+ this.isolatethread.deref(),
82
+ );
83
+
84
+ if (ret !== 0) {
85
+ throw new Error("Failed to tear down isolate.");
86
+ }
87
+ }
88
+ }
89
+
90
+ // Helper function to find the libyamlstar shared library path
91
+ function findLibyamlstarPath() {
92
+ let platform = os.platform();
93
+ let soExtension = platform === 'win32' ? 'dll' : (platform === 'linux' ? 'so' : 'dylib');
94
+ let libyamlstarName = `libyamlstar.${soExtension}.${yamlstarVersion}`;
95
+
96
+ let searchPaths = [];
97
+
98
+ // Add relative path for development (from nodejs/lib/yamlstar to libyamlstar/lib)
99
+ let devPath = path.join(__dirname, '..', '..', '..', 'libyamlstar', 'lib');
100
+ if (fs.existsSync(devPath)) {
101
+ searchPaths.push(devPath);
102
+ }
103
+
104
+ // Add LD_LIBRARY_PATH
105
+ if (process.env.LD_LIBRARY_PATH) {
106
+ searchPaths.push(...process.env.LD_LIBRARY_PATH.split(':'));
107
+ }
108
+
109
+ // Add system paths
110
+ searchPaths.push('/usr/local/lib', path.join(os.homedir(), '.local', 'lib'));
111
+
112
+ for (let p of searchPaths) {
113
+ let fullPath = path.join(p, libyamlstarName);
114
+ if (fs.existsSync(fullPath)) {
115
+ return fullPath;
116
+ }
117
+ }
118
+
119
+ throw new Error(
120
+ `Shared library file '${libyamlstarName}' not found
121
+ Search paths: ${searchPaths.join(':')}
122
+ Build with: cd libyamlstar && make native`);
123
+ }
124
+
125
+ module.exports = YAMLStar;
package/package.json CHANGED
@@ -1,20 +1,27 @@
1
1
  {
2
2
  "name": "yamlstar",
3
- "version": "0.0.1",
4
- "description": "A cross-language, common API YAML reference framework",
5
- "main": "index.js",
3
+ "version": "0.1.2",
4
+ "description": "YAML 1.2 loader - Node.js bindings",
5
+ "main": "lib/yamlstar/index.js",
6
+ "author": "Ingy döt Net",
7
+ "license": "MIT",
8
+ "dependencies": {
9
+ "@makeomatic/ffi-napi": "^4.2.0",
10
+ "ref-napi": "^3.0.3"
11
+ },
12
+ "engines": {
13
+ "node": ">=18.0.0"
14
+ },
6
15
  "directories": {
7
- "lib": "lib"
16
+ "test": "test"
8
17
  },
9
18
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
19
+ "test": "make test"
11
20
  },
12
21
  "repository": {
13
22
  "type": "git",
14
23
  "url": "git+https://github.com/yaml/yamlstar.git"
15
24
  },
16
- "author": "Ingy döt Net",
17
- "license": "MIT",
18
25
  "bugs": {
19
26
  "url": "https://github.com/yaml/yamlstar/issues"
20
27
  },
package/test/test.js ADDED
@@ -0,0 +1,146 @@
1
+ const YAMLStar = require('../lib/yamlstar');
2
+ const assert = require('assert');
3
+
4
+ // Helper function for deep equality check
5
+ function deepEqual(actual, expected, message) {
6
+ try {
7
+ assert.deepStrictEqual(actual, expected);
8
+ console.log(`✓ ${message}`);
9
+ } catch (e) {
10
+ console.error(`✗ ${message}`);
11
+ console.error(` Expected: ${JSON.stringify(expected)}`);
12
+ console.error(` Actual: ${JSON.stringify(actual)}`);
13
+ process.exit(1);
14
+ }
15
+ }
16
+
17
+ // Create a YAMLStar instance
18
+ const ys = new YAMLStar();
19
+
20
+ console.log('Running YAMLStar tests...\n');
21
+
22
+ // Test simple scalar
23
+ deepEqual(ys.load('hello'), 'hello', 'Load simple scalar');
24
+
25
+ // Test integer
26
+ deepEqual(ys.load('42'), 42, 'Load integer');
27
+
28
+ // Test float
29
+ deepEqual(ys.load('3.14'), 3.14, 'Load float');
30
+
31
+ // Test boolean true
32
+ deepEqual(ys.load('true'), true, 'Load boolean true');
33
+
34
+ // Test boolean false
35
+ deepEqual(ys.load('false'), false, 'Load boolean false');
36
+
37
+ // Test null
38
+ deepEqual(ys.load('null'), null, 'Load null');
39
+
40
+ // Test simple mapping
41
+ deepEqual(ys.load('key: value'), {key: 'value'}, 'Load simple mapping');
42
+
43
+ // Test nested mapping
44
+ deepEqual(
45
+ ys.load('outer:\n inner: value'),
46
+ {outer: {inner: 'value'}},
47
+ 'Load nested mapping'
48
+ );
49
+
50
+ // Test mapping with multiple keys
51
+ deepEqual(
52
+ ys.load('key1: value1\nkey2: value2\nkey3: value3'),
53
+ {key1: 'value1', key2: 'value2', key3: 'value3'},
54
+ 'Load mapping with multiple keys'
55
+ );
56
+
57
+ // Test simple sequence
58
+ deepEqual(
59
+ ys.load('- item1\n- item2\n- item3'),
60
+ ['item1', 'item2', 'item3'],
61
+ 'Load simple sequence'
62
+ );
63
+
64
+ // Test flow sequence
65
+ deepEqual(ys.load('[a, b, c]'), ['a', 'b', 'c'], 'Load flow sequence');
66
+
67
+ // Test type coercion
68
+ deepEqual(
69
+ ys.load('string: hello\ninteger: 42\nfloat: 3.14\nbool_true: true\nbool_false: false\nnull_value: null'),
70
+ {
71
+ string: 'hello',
72
+ integer: 42,
73
+ float: 3.14,
74
+ bool_true: true,
75
+ bool_false: false,
76
+ null_value: null
77
+ },
78
+ 'Load with type coercion'
79
+ );
80
+
81
+ // Test sequence of mappings
82
+ deepEqual(
83
+ ys.load('- name: Alice\n age: 30\n- name: Bob\n age: 25'),
84
+ [
85
+ {name: 'Alice', age: 30},
86
+ {name: 'Bob', age: 25}
87
+ ],
88
+ 'Load sequence of mappings'
89
+ );
90
+
91
+ // Test mapping with sequence values
92
+ deepEqual(
93
+ ys.load('fruits:\n - apple\n - banana\ncolors:\n - red\n - blue'),
94
+ {
95
+ fruits: ['apple', 'banana'],
96
+ colors: ['red', 'blue']
97
+ },
98
+ 'Load mapping with sequence values'
99
+ );
100
+
101
+ // Test loadAll with single document
102
+ deepEqual(ys.loadAll('hello'), ['hello'], 'LoadAll with single document');
103
+
104
+ // Test loadAll with multiple documents
105
+ deepEqual(
106
+ ys.loadAll('---\ndoc1\n---\ndoc2\n---\ndoc3'),
107
+ ['doc1', 'doc2', 'doc3'],
108
+ 'LoadAll with multiple documents'
109
+ );
110
+
111
+ // Test loadAll with explicit markers
112
+ deepEqual(
113
+ ys.loadAll('---\na: 1\n...\n---\nb: 2\n...'),
114
+ [{a: 1}, {b: 2}],
115
+ 'LoadAll with explicit markers'
116
+ );
117
+
118
+ // Test version
119
+ const version = ys.version();
120
+ assert(typeof version === 'string' && version.length > 0, 'Version returns a string');
121
+ console.log(`✓ Version returns a string: ${version}`);
122
+
123
+ // Test error handling with malformed YAML
124
+ try {
125
+ ys.load('key: "unclosed');
126
+ console.error('✗ Should have thrown error on malformed YAML');
127
+ process.exit(1);
128
+ } catch (e) {
129
+ assert(e.message.includes('libyamlstar'), 'Error message includes libyamlstar');
130
+ console.log('✓ Dies with libyamlstar error on malformed YAML');
131
+ }
132
+
133
+ // Test empty document
134
+ deepEqual(ys.load(''), null, 'Load empty document');
135
+
136
+ // Test whitespace only
137
+ deepEqual(ys.load(' \n \n '), null, 'Load whitespace only');
138
+
139
+ // Test quoted strings
140
+ deepEqual(ys.load("'hello world'"), 'hello world', 'Load single-quoted string');
141
+ deepEqual(ys.load('"hello world"'), 'hello world', 'Load double-quoted string');
142
+
143
+ // Clean up
144
+ ys.close();
145
+
146
+ console.log('\n✓ All tests passed!');
package/Changes DELETED
@@ -1,5 +0,0 @@
1
- ---
2
- version: 0.0.1
3
- date: Mon Apr 17 12:19:29 PM PDT 2023
4
- changes:
5
- - First release
@@ -1,4 +0,0 @@
1
- require 'yaml'
2
-
3
- class yamlstar
4
- version: '0.0.1'