@weborigami/language 0.5.7 → 0.5.8
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/package.json +3 -3
- package/src/handlers/handlers.js +2 -0
- package/src/handlers/sh_handler.js +65 -0
- package/src/handlers/tsv_handler.js +54 -0
- package/test/handlers/sh_handler.test.js +14 -0
- package/test/handlers/tsv_handler.test.js +28 -0
- /package/test/handlers/{csv.handler.test.js → csv_handler.test.js} +0 -0
- /package/test/handlers/{jpeg.handler.test.js → jpeg_handler.test.js} +0 -0
- /package/test/handlers/{js.handler.test.js → js_handler.test.js} +0 -0
- /package/test/handlers/{json.handler.test.js → json_handler.test.js} +0 -0
- /package/test/handlers/{ori.handler.test.js → ori_handler.test.js} +0 -0
- /package/test/handlers/{oridocument.handler.test.js → oridocument_handler.test.js} +0 -0
- /package/test/handlers/{txt.handler.test.js → txt_handler.test.js} +0 -0
- /package/test/handlers/{wasm.handler.test.js → wasm_handler.test.js} +0 -0
- /package/test/handlers/{yaml.handler.test.js → yaml_handler.test.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"typescript": "5.9.2"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.5.
|
|
15
|
-
"@weborigami/types": "0.5.
|
|
14
|
+
"@weborigami/async-tree": "0.5.8",
|
|
15
|
+
"@weborigami/types": "0.5.8",
|
|
16
16
|
"exif-parser": "0.1.12",
|
|
17
17
|
"watcher": "2.3.1",
|
|
18
18
|
"yaml": "2.8.1"
|
package/src/handlers/handlers.js
CHANGED
|
@@ -27,6 +27,8 @@ export { default as jpg_handler } from "./jpg_handler.js";
|
|
|
27
27
|
export { default as json_handler } from "./json_handler.js";
|
|
28
28
|
export { default as md_handler } from "./md_handler.js";
|
|
29
29
|
export { default as mjs_handler } from "./mjs_handler.js";
|
|
30
|
+
export { default as sh_handler } from "./sh_handler.js";
|
|
31
|
+
export { default as tsv_handler } from "./tsv_handler.js";
|
|
30
32
|
export { default as wasm_handler } from "./wasm_handler.js";
|
|
31
33
|
export { default as xhtml_handler } from "./xhtml_handler.js";
|
|
32
34
|
export { default as yaml_handler } from "./yaml_handler.js";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { toString } from "@weborigami/async-tree";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shell script file extension handler
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
mediaType: "text/plain",
|
|
9
|
+
|
|
10
|
+
/** @type {import("@weborigami/async-tree").UnpackFunction} */
|
|
11
|
+
async unpack(packed) {
|
|
12
|
+
const scriptText = toString(packed);
|
|
13
|
+
|
|
14
|
+
if (scriptText === null) {
|
|
15
|
+
throw new Error(".sh handler: input isn't text");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return async (input) => {
|
|
19
|
+
return runShellScript(scriptText, input);
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Run arbitrary shell script text in /bin/sh and feed it stdin.
|
|
26
|
+
* Supports multiple commands, pipelines, redirects, etc.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} scriptText - Shell code (may contain newlines/side effects)
|
|
29
|
+
* @param {string} inputText - Text to pipe to the script's stdin
|
|
30
|
+
* @returns {Promise<string>}
|
|
31
|
+
*/
|
|
32
|
+
function runShellScript(scriptText, inputText) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
// Use sh -c "<scriptText>" so stdin is free for inputText
|
|
35
|
+
const child = spawn("sh", ["-c", scriptText], {
|
|
36
|
+
env: { ...process.env },
|
|
37
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let stdout = "";
|
|
41
|
+
let stderr = "";
|
|
42
|
+
|
|
43
|
+
child.stdout.on("data", (c) => (stdout += c));
|
|
44
|
+
child.stderr.on("data", (c) => (stderr += c));
|
|
45
|
+
|
|
46
|
+
child.on("error", reject);
|
|
47
|
+
|
|
48
|
+
child.on("close", (code) => {
|
|
49
|
+
if (code !== 0) {
|
|
50
|
+
/** @type {any} */
|
|
51
|
+
const err = new Error(
|
|
52
|
+
`Shell exited with code ${code}${stderr ? `: ${stderr}` : ""}`
|
|
53
|
+
);
|
|
54
|
+
err.code = code;
|
|
55
|
+
err.stdout = stdout;
|
|
56
|
+
err.stderr = stderr;
|
|
57
|
+
return reject(err);
|
|
58
|
+
}
|
|
59
|
+
resolve(stdout);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Feed the input to the script's stdin and close it
|
|
63
|
+
child.stdin.end(inputText);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { symbols, toString } from "@weborigami/async-tree";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
mediaType: "text/csv",
|
|
5
|
+
|
|
6
|
+
unpack(packed, options = {}) {
|
|
7
|
+
const parent = options.parent ?? null;
|
|
8
|
+
const text = toString(packed);
|
|
9
|
+
if (text === null) {
|
|
10
|
+
throw new TypeError(".tsv handler can only unpack text");
|
|
11
|
+
}
|
|
12
|
+
const data = tsvParse(text);
|
|
13
|
+
// Define `parent` as non-enumerable property
|
|
14
|
+
Object.defineProperty(data, symbols.parent, {
|
|
15
|
+
configurable: true,
|
|
16
|
+
enumerable: false,
|
|
17
|
+
value: parent,
|
|
18
|
+
writable: true,
|
|
19
|
+
});
|
|
20
|
+
return data;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse text as tab-separated values (TSV) format into an array of objects.
|
|
26
|
+
*
|
|
27
|
+
* This assumes the presence of a header row, and accepts both CRLF and LF line
|
|
28
|
+
* endings.
|
|
29
|
+
*
|
|
30
|
+
* Blank lines are ignored.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} text
|
|
33
|
+
* @returns {any[]}
|
|
34
|
+
*/
|
|
35
|
+
function tsvParse(text) {
|
|
36
|
+
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
37
|
+
if (lines.length === 0) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const headers = lines[0].split("\t");
|
|
42
|
+
const data = [];
|
|
43
|
+
|
|
44
|
+
for (let i = 1; i < lines.length; i++) {
|
|
45
|
+
const values = lines[i].split("\t");
|
|
46
|
+
const entry = {};
|
|
47
|
+
for (let j = 0; j < headers.length; j++) {
|
|
48
|
+
entry[headers[j]] = values[j] ?? "";
|
|
49
|
+
}
|
|
50
|
+
data.push(entry);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import sh_handler from "../../src/handlers/sh_handler.js";
|
|
4
|
+
|
|
5
|
+
describe(".sh handler", () => {
|
|
6
|
+
test("invokes shell commands", async () => {
|
|
7
|
+
const text = `echo Hello
|
|
8
|
+
cat
|
|
9
|
+
`;
|
|
10
|
+
const fn = await sh_handler.unpack(text);
|
|
11
|
+
const output = await fn("Input text");
|
|
12
|
+
assert.equal(output, "Hello\nInput text");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import tsv_handler from "../../src/handlers/tsv_handler.js";
|
|
4
|
+
|
|
5
|
+
describe(".tsv handler", () => {
|
|
6
|
+
test("parses TSV text into array of objects", () => {
|
|
7
|
+
const TSVText = `name age city
|
|
8
|
+
Alice\t30\tNew York, NY
|
|
9
|
+
Bob\t25\tLos Angeles
|
|
10
|
+
Carol\t22\tChicago`;
|
|
11
|
+
const result = tsv_handler.unpack(TSVText);
|
|
12
|
+
assert.deepStrictEqual(result, [
|
|
13
|
+
{ name: "Alice", age: "30", city: "New York, NY" },
|
|
14
|
+
{ name: "Bob", age: "25", city: "Los Angeles" },
|
|
15
|
+
{ name: "Carol", age: "22", city: "Chicago" },
|
|
16
|
+
]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("handles CRLF line endings", () => {
|
|
20
|
+
const textCRLF = `name\tage\tcity\r\nAlice\t30\tNew York, NY\r\nBob\t25\tLos Angeles\r\n`;
|
|
21
|
+
const expected = [
|
|
22
|
+
{ name: "Alice", age: "30", city: "New York, NY" },
|
|
23
|
+
{ name: "Bob", age: "25", city: "Los Angeles" },
|
|
24
|
+
];
|
|
25
|
+
const result = tsv_handler.unpack(textCRLF);
|
|
26
|
+
assert.deepStrictEqual(result, expected);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|