eyeling 1.18.2 → 1.19.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/HANDBOOK.md CHANGED
@@ -1777,12 +1777,16 @@ Install from npm:
1777
1777
  npm i eyeling
1778
1778
  ```
1779
1779
 
1780
- Run a file:
1780
+ Run a self-contained example from stdin:
1781
1781
 
1782
1782
  ```bash
1783
- npx eyeling examples/socrates.n3
1783
+ echo '@prefix : <http://example.org/> .
1784
+ :Socrates a :Man .
1785
+ { ?x a :Man } => { ?x a :Mortal } .' | npx eyeling
1784
1786
  ```
1785
1787
 
1788
+ You can also pass a file path, or `-` to read explicitly from stdin.
1789
+
1786
1790
  Show the available options:
1787
1791
 
1788
1792
  ```bash
@@ -1823,6 +1827,8 @@ The current CLI supports a small set of flags (see `lib/cli.js`):
1823
1827
  - `-t`, `--stream` — stream derived triples as soon as they are derived.
1824
1828
  - `-v`, `--version` — print version and exit.
1825
1829
  - `-h`, `--help` — show usage.
1830
+ - With no positional argument, Eyeling reads from stdin when input is piped.
1831
+ - Use `-` as the input path to read explicitly from stdin.
1826
1832
 
1827
1833
  ### 14.3 `lib/entry.js`: bundler-friendly exports
1828
1834
 
package/README.md CHANGED
@@ -7,8 +7,9 @@ A compact [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
7
7
  ## Quick start
8
8
 
9
9
  ```bash
10
- npm i eyeling
11
- npx eyeling examples/socrates.n3
10
+ echo '@prefix : <http://example.org/> .
11
+ :Socrates a :Man .
12
+ { ?x a :Man } => { ?x a :Mortal } .' | npx eyeling
12
13
  ```
13
14
 
14
15
  ## Read more
package/eyeling.js CHANGED
@@ -4536,6 +4536,11 @@ function formatN3SyntaxError(err, text, path) {
4536
4536
  }
4537
4537
 
4538
4538
  // CLI entry point (invoked when eyeling.js is run directly)
4539
+ function readTextFromStdinSync() {
4540
+ const fs = require('node:fs');
4541
+ return fs.readFileSync(0, { encoding: 'utf8' });
4542
+ }
4543
+
4539
4544
  function main() {
4540
4545
  // Drop "node" and script name; keep only user-provided args
4541
4546
  // Expand combined short options: -pt == -p -t
@@ -4555,7 +4560,8 @@ function main() {
4555
4560
 
4556
4561
  function printHelp(toStderr = false) {
4557
4562
  const msg =
4558
- `Usage: ${prog} [options] <file.n3>\n\n` +
4563
+ `Usage: ${prog} [options] [file.n3|-]\n\n` +
4564
+ `When no file is given and stdin is piped, read N3 from stdin.\n\n` +
4559
4565
  `Options:\n` +
4560
4566
  ` -a, --ast Print parsed AST as JSON and exit.\n` +
4561
4567
  ` --builtin <module.js> Load a custom builtin module (repeatable).\n` +
@@ -4626,12 +4632,13 @@ function main() {
4626
4632
  }
4627
4633
 
4628
4634
  // Positional args (the N3 file)
4629
- if (positional.length === 0) {
4635
+ const useImplicitStdin = positional.length === 0 && !process.stdin.isTTY;
4636
+ if (positional.length === 0 && !useImplicitStdin) {
4630
4637
  printHelp(false);
4631
4638
  process.exit(0);
4632
4639
  }
4633
- if (positional.length !== 1) {
4634
- console.error('Error: expected exactly one input <file.n3>.');
4640
+ if (positional.length > 1) {
4641
+ console.error('Error: expected at most one input [file.n3|-].');
4635
4642
  printHelp(true);
4636
4643
  process.exit(1);
4637
4644
  }
@@ -4646,13 +4653,17 @@ function main() {
4646
4653
  }
4647
4654
  }
4648
4655
 
4649
- const filePath = positional[0];
4656
+ const sourceLabel = useImplicitStdin || positional[0] === '-' ? '<stdin>' : positional[0];
4650
4657
  let text;
4651
4658
  try {
4652
- const fs = require('fs');
4653
- text = fs.readFileSync(filePath, { encoding: 'utf8' });
4659
+ if (sourceLabel === '<stdin>') text = readTextFromStdinSync();
4660
+ else {
4661
+ const fs = require('node:fs');
4662
+ text = fs.readFileSync(sourceLabel, { encoding: 'utf8' });
4663
+ }
4654
4664
  } catch (e) {
4655
- console.error(`Error reading file ${JSON.stringify(filePath)}: ${e.message}`);
4665
+ if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
4666
+ else console.error(`Error reading file ${JSON.stringify(sourceLabel)}: ${e.message}`);
4656
4667
  process.exit(1);
4657
4668
  }
4658
4669
 
@@ -4666,7 +4677,7 @@ function main() {
4666
4677
  engine.setTracePrefixes(prefixes);
4667
4678
  } catch (e) {
4668
4679
  if (e && e.name === 'N3SyntaxError') {
4669
- console.error(formatN3SyntaxError(e, text, filePath));
4680
+ console.error(formatN3SyntaxError(e, text, sourceLabel));
4670
4681
  process.exit(1);
4671
4682
  }
4672
4683
  throw e;
package/lib/cli.js CHANGED
@@ -45,6 +45,11 @@ function formatN3SyntaxError(err, text, path) {
45
45
  }
46
46
 
47
47
  // CLI entry point (invoked when eyeling.js is run directly)
48
+ function readTextFromStdinSync() {
49
+ const fs = require('node:fs');
50
+ return fs.readFileSync(0, { encoding: 'utf8' });
51
+ }
52
+
48
53
  function main() {
49
54
  // Drop "node" and script name; keep only user-provided args
50
55
  // Expand combined short options: -pt == -p -t
@@ -64,7 +69,8 @@ function main() {
64
69
 
65
70
  function printHelp(toStderr = false) {
66
71
  const msg =
67
- `Usage: ${prog} [options] <file.n3>\n\n` +
72
+ `Usage: ${prog} [options] [file.n3|-]\n\n` +
73
+ `When no file is given and stdin is piped, read N3 from stdin.\n\n` +
68
74
  `Options:\n` +
69
75
  ` -a, --ast Print parsed AST as JSON and exit.\n` +
70
76
  ` --builtin <module.js> Load a custom builtin module (repeatable).\n` +
@@ -135,12 +141,13 @@ function main() {
135
141
  }
136
142
 
137
143
  // Positional args (the N3 file)
138
- if (positional.length === 0) {
144
+ const useImplicitStdin = positional.length === 0 && !process.stdin.isTTY;
145
+ if (positional.length === 0 && !useImplicitStdin) {
139
146
  printHelp(false);
140
147
  process.exit(0);
141
148
  }
142
- if (positional.length !== 1) {
143
- console.error('Error: expected exactly one input <file.n3>.');
149
+ if (positional.length > 1) {
150
+ console.error('Error: expected at most one input [file.n3|-].');
144
151
  printHelp(true);
145
152
  process.exit(1);
146
153
  }
@@ -155,13 +162,17 @@ function main() {
155
162
  }
156
163
  }
157
164
 
158
- const filePath = positional[0];
165
+ const sourceLabel = useImplicitStdin || positional[0] === '-' ? '<stdin>' : positional[0];
159
166
  let text;
160
167
  try {
161
- const fs = require('fs');
162
- text = fs.readFileSync(filePath, { encoding: 'utf8' });
168
+ if (sourceLabel === '<stdin>') text = readTextFromStdinSync();
169
+ else {
170
+ const fs = require('node:fs');
171
+ text = fs.readFileSync(sourceLabel, { encoding: 'utf8' });
172
+ }
163
173
  } catch (e) {
164
- console.error(`Error reading file ${JSON.stringify(filePath)}: ${e.message}`);
174
+ if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
175
+ else console.error(`Error reading file ${JSON.stringify(sourceLabel)}: ${e.message}`);
165
176
  process.exit(1);
166
177
  }
167
178
 
@@ -175,7 +186,7 @@ function main() {
175
186
  engine.setTracePrefixes(prefixes);
176
187
  } catch (e) {
177
188
  if (e && e.name === 'N3SyntaxError') {
178
- console.error(formatN3SyntaxError(e, text, filePath));
189
+ console.error(formatN3SyntaxError(e, text, sourceLabel));
179
190
  process.exit(1);
180
191
  }
181
192
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.18.2",
3
+ "version": "1.19.0",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -1762,6 +1762,55 @@ _:x :hates { _:foo :making :mess }.
1762
1762
  },
1763
1763
  expect: [/http:\/\/example\.org\/ancestor/m],
1764
1764
  },
1765
+ {
1766
+ name: '67 CLI stdin: accepts piped N3 when no file argument is given',
1767
+ run() {
1768
+ const input = `@prefix : <http://example.org/> .
1769
+ :Socrates a :Man .
1770
+ { ?x a :Man } => { ?x a :Mortal } .
1771
+ `;
1772
+ const r = spawnSync(process.execPath, [path.join(ROOT, 'eyeling.js')], {
1773
+ input,
1774
+ encoding: 'utf8',
1775
+ stdio: ['pipe', 'pipe', 'pipe'],
1776
+ });
1777
+ if (r.error) throw r.error;
1778
+ if (r.status !== 0) {
1779
+ const err = new Error(`CLI failed with exit ${r.status}`);
1780
+ err.code = r.status;
1781
+ err.stdout = r.stdout;
1782
+ err.stderr = r.stderr;
1783
+ throw err;
1784
+ }
1785
+ return r.stdout;
1786
+ },
1787
+ expect: [/:(?:Socrates)\s+a\s+:(?:Mortal)\s*\./],
1788
+ },
1789
+ {
1790
+ name: '68 CLI stdin: accepts explicit - for stdin',
1791
+ run() {
1792
+ const input = `@prefix : <http://example.org/> .
1793
+ :Socrates a :Man .
1794
+ { ?x a :Man } => { ?x a :Mortal } .
1795
+ `;
1796
+ const r = spawnSync(process.execPath, [path.join(ROOT, 'eyeling.js'), '-'], {
1797
+ input,
1798
+ encoding: 'utf8',
1799
+ stdio: ['pipe', 'pipe', 'pipe'],
1800
+ });
1801
+ if (r.error) throw r.error;
1802
+ if (r.status !== 0) {
1803
+ const err = new Error(`CLI failed with exit ${r.status}`);
1804
+ err.code = r.status;
1805
+ err.stdout = r.stdout;
1806
+ err.stderr = r.stderr;
1807
+ throw err;
1808
+ }
1809
+ return r.stdout;
1810
+ },
1811
+ expect: [/:(?:Socrates)\s+a\s+:(?:Mortal)\s*\./],
1812
+ },
1813
+
1765
1814
  {
1766
1815
  name: '240 custom builtin module can be loaded via --builtin',
1767
1816
  run() {