@grida/fx 0.0.0-stage → 0.1.0-alpha.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 +93 -2
- package/bin/grida-fx.js +5 -0
- package/dist/api.d.ts +128 -0
- package/dist/api.js +319 -0
- package/dist/binary.d.ts +49 -0
- package/dist/binary.js +119 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.js +114 -0
- package/dist/documents.d.ts +213 -0
- package/dist/documents.js +7 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +130 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +27 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +12 -0
- package/dist/platforms.d.ts +31 -0
- package/dist/platforms.js +61 -0
- package/dist/request.d.ts +62 -0
- package/dist/request.js +142 -0
- package/dist/workflow.d.ts +190 -0
- package/dist/workflow.js +33 -0
- package/dist/yaml.d.ts +21 -0
- package/dist/yaml.js +288 -0
- package/package.json +50 -4
package/dist/yaml.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing YAML that the engine reads: the strict subset of `spec/yaml.md`, written as the engine
|
|
3
|
+
* writes it ("Writing YAML", `crates/grida-fx-core/src/yaml/write.rs`).
|
|
4
|
+
*
|
|
5
|
+
* Block style, two-space indentation, keys in the order the object holds them. A string is
|
|
6
|
+
* written plain when, read back as a plain scalar, it is that same string; otherwise it is
|
|
7
|
+
* double-quoted with JSON-style escapes. So `on`, `017`, `2026-10-05`, `1e3`, a digest like
|
|
8
|
+
* `123e4567…`, `''`, and text with `: `, ` #` or a leading indicator are all quoted. Numbers are
|
|
9
|
+
* written in their canonical form (ECMAScript's, as JCS writes them). The text ends with one
|
|
10
|
+
* line feed.
|
|
11
|
+
*
|
|
12
|
+
* Only FX values are written (`spec/identity.md` §1): `null`, booleans, finite numbers, strings
|
|
13
|
+
* without lone surrogates, arrays, and plain objects, nested at most 512 deep. An object member
|
|
14
|
+
* whose value is `undefined` is left out, as JSON leaves it out; anything else is a `TypeError`
|
|
15
|
+
* naming where it is. A key longer than 1024 characters cannot be written (YAML's limit for an
|
|
16
|
+
* implicit key).
|
|
17
|
+
*/
|
|
18
|
+
/** How deep values may nest (`spec/yaml.md`, "Nesting"). */
|
|
19
|
+
const MAX_DEPTH = 512;
|
|
20
|
+
/** The longest key YAML writes as an implicit key. */
|
|
21
|
+
const MAX_KEY = 1024;
|
|
22
|
+
/** Writes `value` as a YAML document the engine reads back as the same value. */
|
|
23
|
+
export function toYaml(value) {
|
|
24
|
+
const plain = check(value, "the value", 0);
|
|
25
|
+
if (isObject(plain) && Object.keys(plain).length > 0) {
|
|
26
|
+
return writeMapping(plain, 0, false);
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(plain) && plain.length > 0) {
|
|
29
|
+
return writeSequence(plain, 0, false);
|
|
30
|
+
}
|
|
31
|
+
return `${scalarText(plain)}\n`;
|
|
32
|
+
}
|
|
33
|
+
/** Whether a string must be quoted to read back as itself. */
|
|
34
|
+
export function needsQuotes(text) {
|
|
35
|
+
// Anything a plain scalar would read as something else, or refuse.
|
|
36
|
+
if (!resolvesToItself(text)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// YAML 1.1's merge and value types: FX reads them as text, YAML 1.1 readers do not, and a plain
|
|
40
|
+
// `<<` key is a merge key.
|
|
41
|
+
if (text === "<<" || text === "=") {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
// A leading indicator starts something other than a plain scalar (or might, for `-`, `?` and
|
|
45
|
+
// `:`); `---` and `...` at the start of a line are document markers.
|
|
46
|
+
if ("-?:,[]{}#&*!|>'\"%@`".includes(text[0] ?? "") || text.startsWith("...")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Plain scalars lose leading and trailing white space; `: ` (or a final `:`) makes a key and
|
|
50
|
+
// ` #` starts a comment.
|
|
51
|
+
if (text.startsWith(" ") || text.endsWith(" ") || text.endsWith(":")) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (text.includes(": ") || text.includes(" #")) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// Line breaks, tabs and other characters a plain scalar cannot hold as they are.
|
|
58
|
+
for (const char of text) {
|
|
59
|
+
if (needsEscape(char)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// ------------------------------------------------------------------------------------------------
|
|
66
|
+
// Plain-scalar resolution (spec/yaml.md "Values"), reduced to the question the writer asks.
|
|
67
|
+
/** Whether a plain scalar reads back as the string it is (not null, a boolean, a number, or
|
|
68
|
+
* refused). */
|
|
69
|
+
function resolvesToItself(text) {
|
|
70
|
+
if (text === "" || text === "~" || text === "null" || text === "true" || text === "false") {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// Every decimal form, integer or float, leading zeros included: a number, a refused number, or
|
|
74
|
+
// an ambiguous form.
|
|
75
|
+
if (isDecimal(text)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return !isAmbiguous(text);
|
|
79
|
+
}
|
|
80
|
+
/** The ambiguous forms of yaml.md, beyond the decimal ones. */
|
|
81
|
+
function isAmbiguous(text) {
|
|
82
|
+
return (isWord(text) ||
|
|
83
|
+
isRadix(text) ||
|
|
84
|
+
(text.includes("_") && isNumberLike(text.replaceAll("_", ""))) ||
|
|
85
|
+
isSexagesimal(text) ||
|
|
86
|
+
isSpecial(text) ||
|
|
87
|
+
isTimestamp(text));
|
|
88
|
+
}
|
|
89
|
+
/** `yes|no|on|off|true|false|null` in any ASCII letter case. */
|
|
90
|
+
function isWord(text) {
|
|
91
|
+
const lower = asciiLower(text);
|
|
92
|
+
return ["yes", "no", "on", "off", "true", "false", "null"].includes(lower);
|
|
93
|
+
}
|
|
94
|
+
/** `[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*|[0-9]+)(?:[eE][-+]?[0-9]+)?`, leading zeros included. */
|
|
95
|
+
function isDecimal(text) {
|
|
96
|
+
return /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*|[0-9]+)(?:[eE][-+]?[0-9]+)?$/.test(text);
|
|
97
|
+
}
|
|
98
|
+
/** `[-+]?0(?:[xX][0-9a-fA-F_]+|[oO][0-7_]+|[bB][01_]+)` */
|
|
99
|
+
function isRadix(text) {
|
|
100
|
+
return /^[-+]?0(?:[xX][0-9a-fA-F_]+|[oO][0-7_]+|[bB][01_]+)$/.test(text);
|
|
101
|
+
}
|
|
102
|
+
/** Any numeric reading of any YAML version: decimal, radix, sexagesimal. */
|
|
103
|
+
function isNumberLike(text) {
|
|
104
|
+
return isDecimal(text) || isRadix(text) || isSexagesimal(text);
|
|
105
|
+
}
|
|
106
|
+
/** `[-+]?[0-9]+(?::[0-9]+)+(?:\.[0-9]*)?` */
|
|
107
|
+
function isSexagesimal(text) {
|
|
108
|
+
return /^[-+]?[0-9]+(?::[0-9]+)+(?:\.[0-9]*)?$/.test(text);
|
|
109
|
+
}
|
|
110
|
+
/** `[-+]?\.inf` or `\.nan`, in any ASCII letter case. */
|
|
111
|
+
function isSpecial(text) {
|
|
112
|
+
const lower = asciiLower(text);
|
|
113
|
+
return /^[-+]?\.inf$/.test(lower) || lower === ".nan";
|
|
114
|
+
}
|
|
115
|
+
/** YAML 1.1's timestamp type: `YYYY-MM-DD`, or a date with one- or two-digit month and day, `T`,
|
|
116
|
+
* `t` or spaces and tabs, a time `H[H]:MM:SS[.fraction]`, then an optional zone (`Z` or
|
|
117
|
+
* `±H[H][:MM]`) after optional spaces and tabs. */
|
|
118
|
+
function isTimestamp(text) {
|
|
119
|
+
return (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(text) ||
|
|
120
|
+
/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}(?:[Tt]|[ \t]+)[0-9]{1,2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9]{1,2}(?::[0-9]{2})?))?$/.test(text));
|
|
121
|
+
}
|
|
122
|
+
function asciiLower(text) {
|
|
123
|
+
return text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
124
|
+
}
|
|
125
|
+
// ------------------------------------------------------------------------------------------------
|
|
126
|
+
// Writing
|
|
127
|
+
/** Characters written as an escape inside double quotes: C0 and C1 controls (tab and line feed
|
|
128
|
+
* included), DEL, U+0085, U+2028, U+2029, the byte-order mark, and U+FFFE and U+FFFF. */
|
|
129
|
+
function needsEscape(char) {
|
|
130
|
+
const code = char.codePointAt(0) ?? 0;
|
|
131
|
+
return (code <= 0x1f ||
|
|
132
|
+
(code >= 0x7f && code <= 0x9f) ||
|
|
133
|
+
code === 0x2028 ||
|
|
134
|
+
code === 0x2029 ||
|
|
135
|
+
code === 0xfeff ||
|
|
136
|
+
code === 0xfffe ||
|
|
137
|
+
code === 0xffff);
|
|
138
|
+
}
|
|
139
|
+
function writeMapping(map, indent, inline) {
|
|
140
|
+
let out = "";
|
|
141
|
+
Object.entries(map).forEach(([key, item], index) => {
|
|
142
|
+
if (index > 0 || !inline) {
|
|
143
|
+
out += " ".repeat(indent);
|
|
144
|
+
}
|
|
145
|
+
out += `${keyText(key)}:`;
|
|
146
|
+
if (isObject(item) && Object.keys(item).length > 0) {
|
|
147
|
+
out += `\n${writeMapping(item, indent + 2, false)}`;
|
|
148
|
+
}
|
|
149
|
+
else if (Array.isArray(item) && item.length > 0) {
|
|
150
|
+
out += `\n${writeSequence(item, indent + 2, false)}`;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
out += ` ${scalarText(item)}\n`;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
function writeSequence(items, indent, inline) {
|
|
159
|
+
let out = "";
|
|
160
|
+
items.forEach((item, index) => {
|
|
161
|
+
if (index > 0 || !inline) {
|
|
162
|
+
out += " ".repeat(indent);
|
|
163
|
+
}
|
|
164
|
+
out += "-";
|
|
165
|
+
if (isObject(item) && Object.keys(item).length > 0) {
|
|
166
|
+
out += ` ${writeMapping(item, indent + 2, true)}`;
|
|
167
|
+
}
|
|
168
|
+
else if (Array.isArray(item) && item.length > 0) {
|
|
169
|
+
out += ` ${writeSequence(item, indent + 2, true)}`;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
out += ` ${scalarText(item)}\n`;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
function keyText(key) {
|
|
178
|
+
return needsQuotes(key) ? quoted(key) : key;
|
|
179
|
+
}
|
|
180
|
+
/** A scalar or an empty collection, as it is written after `key: ` or `- `. */
|
|
181
|
+
function scalarText(value) {
|
|
182
|
+
if (value === null) {
|
|
183
|
+
return "null";
|
|
184
|
+
}
|
|
185
|
+
if (typeof value === "boolean") {
|
|
186
|
+
return value ? "true" : "false";
|
|
187
|
+
}
|
|
188
|
+
if (typeof value === "number") {
|
|
189
|
+
// ECMAScript's Number.prototype.toString is JCS's number form; -0 is 0.
|
|
190
|
+
return Object.is(value, -0) ? "0" : String(value);
|
|
191
|
+
}
|
|
192
|
+
if (typeof value === "string") {
|
|
193
|
+
return needsQuotes(value) ? quoted(value) : value;
|
|
194
|
+
}
|
|
195
|
+
return Array.isArray(value) ? "[]" : "{}";
|
|
196
|
+
}
|
|
197
|
+
/** A double-quoted scalar with JSON's escapes (YAML's double-quoted escapes include them all). */
|
|
198
|
+
function quoted(text) {
|
|
199
|
+
let out = '"';
|
|
200
|
+
for (const char of text) {
|
|
201
|
+
switch (char) {
|
|
202
|
+
case '"':
|
|
203
|
+
out += '\\"';
|
|
204
|
+
break;
|
|
205
|
+
case "\\":
|
|
206
|
+
out += "\\\\";
|
|
207
|
+
break;
|
|
208
|
+
case "\b":
|
|
209
|
+
out += "\\b";
|
|
210
|
+
break;
|
|
211
|
+
case "\t":
|
|
212
|
+
out += "\\t";
|
|
213
|
+
break;
|
|
214
|
+
case "\n":
|
|
215
|
+
out += "\\n";
|
|
216
|
+
break;
|
|
217
|
+
case "\f":
|
|
218
|
+
out += "\\f";
|
|
219
|
+
break;
|
|
220
|
+
case "\r":
|
|
221
|
+
out += "\\r";
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
out += needsEscape(char)
|
|
225
|
+
? `\\u${(char.codePointAt(0) ?? 0).toString(16).padStart(4, "0")}`
|
|
226
|
+
: char;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return `${out}"`;
|
|
230
|
+
}
|
|
231
|
+
// ------------------------------------------------------------------------------------------------
|
|
232
|
+
// Checking
|
|
233
|
+
function isObject(value) {
|
|
234
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
const prototype = Object.getPrototypeOf(value);
|
|
238
|
+
return prototype === Object.prototype || prototype === null;
|
|
239
|
+
}
|
|
240
|
+
const LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
241
|
+
/** `value` as a plain FX value, or a `TypeError` naming `where` it is not one. */
|
|
242
|
+
function check(value, where, depth) {
|
|
243
|
+
if (value === null || typeof value === "boolean") {
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
if (typeof value === "number") {
|
|
247
|
+
if (!Number.isFinite(value)) {
|
|
248
|
+
throw new TypeError(`${where} is ${value}, which is not a number FX can hold`);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
if (typeof value === "string") {
|
|
253
|
+
if (LONE_SURROGATE.test(value)) {
|
|
254
|
+
throw new TypeError(`${where} holds a lone surrogate, which is not text FX can hold`);
|
|
255
|
+
}
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
258
|
+
if (depth >= MAX_DEPTH && (Array.isArray(value) || isObject(value))) {
|
|
259
|
+
throw new TypeError(`${where} nests deeper than ${MAX_DEPTH} collections`);
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(value)) {
|
|
262
|
+
return value.map((item, index) => {
|
|
263
|
+
if (item === undefined) {
|
|
264
|
+
throw new TypeError(`${where}[${index}] is undefined`);
|
|
265
|
+
}
|
|
266
|
+
return check(item, `${where}[${index}]`, depth + 1);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (isObject(value)) {
|
|
270
|
+
const out = {};
|
|
271
|
+
for (const [key, item] of Object.entries(value)) {
|
|
272
|
+
if (item === undefined) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const at = `${where}[${JSON.stringify(key)}]`;
|
|
276
|
+
if (LONE_SURROGATE.test(key)) {
|
|
277
|
+
throw new TypeError(`${at}: the key holds a lone surrogate`);
|
|
278
|
+
}
|
|
279
|
+
if ([...key].length > MAX_KEY) {
|
|
280
|
+
throw new TypeError(`${at}: a key longer than ${MAX_KEY} characters cannot be written`);
|
|
281
|
+
}
|
|
282
|
+
out[key] = check(item, at, depth + 1);
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
const kind = typeof value === "object" ? (value.constructor?.name ?? "object") : typeof value;
|
|
287
|
+
throw new TypeError(`${where} is a ${kind}, which is not a value FX can hold`);
|
|
288
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grida/fx",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Grida FX for JavaScript: the grida-fx command, and an SDK to build workflows and drive the engine.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"grida",
|
|
7
|
+
"fx",
|
|
8
|
+
"workflow",
|
|
9
|
+
"generative",
|
|
10
|
+
"assets",
|
|
11
|
+
"pipeline"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/gridaco/fx#readme",
|
|
14
|
+
"bugs": "https://github.com/gridaco/fx/issues",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/gridaco/fx.git",
|
|
18
|
+
"directory": "js/fx"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"grida-fx": "bin/grida-fx.js"
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"bin/grida-fx.js",
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"sideEffects": false,
|
|
42
|
+
"optionalDependencies": {
|
|
43
|
+
"@grida/fx-darwin-arm64": "0.1.0-alpha.1",
|
|
44
|
+
"@grida/fx-darwin-x64": "0.1.0-alpha.1",
|
|
45
|
+
"@grida/fx-linux-arm64-gnu": "0.1.0-alpha.1",
|
|
46
|
+
"@grida/fx-linux-x64-gnu": "0.1.0-alpha.1"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"tag": "next"
|
|
51
|
+
}
|
|
52
|
+
}
|