@fnndsc/brasa 0.29.1 → 0.31.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/dist/api/models.d.ts +2 -0
- package/dist/api/models.js.map +1 -1
- package/dist/buildinfo.json +1 -1
- package/dist/builtins/fs/acl.args.d.ts +53 -18
- package/dist/builtins/fs/acl.args.js +77 -34
- package/dist/builtins/fs/acl.args.js.map +1 -1
- package/dist/builtins/fs/acl.d.ts +16 -5
- package/dist/builtins/fs/acl.js +111 -39
- package/dist/builtins/fs/acl.js.map +1 -1
- package/dist/builtins/fs/cd.js +38 -9
- package/dist/builtins/fs/cd.js.map +1 -1
- package/dist/builtins/fs/edit.d.ts +2 -0
- package/dist/builtins/fs/edit.js +52 -7
- package/dist/builtins/fs/edit.js.map +1 -1
- package/dist/builtins/fs/ls.js +3 -2
- package/dist/builtins/fs/ls.js.map +1 -1
- package/dist/builtins/fs/mkdir.js +48 -1
- package/dist/builtins/fs/mkdir.js.map +1 -1
- package/dist/builtins/fs/mv.js +11 -4
- package/dist/builtins/fs/mv.js.map +1 -1
- package/dist/builtins/fs/rm.d.ts +10 -0
- package/dist/builtins/fs/rm.js +28 -0
- package/dist/builtins/fs/rm.js.map +1 -1
- package/dist/builtins/fs/rmdir.d.ts +24 -0
- package/dist/builtins/fs/rmdir.js +110 -0
- package/dist/builtins/fs/rmdir.js.map +1 -0
- package/dist/builtins/fs/touch.js +3 -2
- package/dist/builtins/fs/touch.js.map +1 -1
- package/dist/builtins/fs/xattr.args.d.ts +52 -0
- package/dist/builtins/fs/xattr.args.js +134 -0
- package/dist/builtins/fs/xattr.args.js.map +1 -0
- package/dist/builtins/fs/xattr.d.ts +26 -0
- package/dist/builtins/fs/xattr.js +98 -0
- package/dist/builtins/fs/xattr.js.map +1 -0
- package/dist/builtins/help.js +86 -19
- package/dist/builtins/help.js.map +1 -1
- package/dist/builtins/index.d.ts +2 -0
- package/dist/builtins/index.js +2 -0
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/net/netstat.d.ts +18 -0
- package/dist/builtins/net/netstat.js +78 -0
- package/dist/builtins/net/netstat.js.map +1 -0
- package/dist/builtins/proc.js +25 -2
- package/dist/builtins/proc.js.map +1 -1
- package/dist/builtins/res/feed.js +12 -34
- package/dist/builtins/res/feed.js.map +1 -1
- package/dist/builtins/utils.js +2 -1
- package/dist/builtins/utils.js.map +1 -1
- package/dist/command-keys.js +1 -1
- package/dist/command-keys.js.map +1 -1
- package/dist/core/dispatch.js +12 -1
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/surface.d.ts +8 -0
- package/dist/core/surface.js.map +1 -1
- package/dist/lib/completer/index.js +1 -1
- package/dist/lib/completer/index.js.map +1 -1
- package/dist/lib/semicolonParser.d.ts +1 -0
- package/dist/lib/semicolonParser.js +14 -1
- package/dist/lib/semicolonParser.js.map +1 -1
- package/dist/lib/vfs/vfs.js +30 -3
- package/dist/lib/vfs/vfs.js.map +1 -1
- package/package.json +5 -5
- package/dist/builtins/res/feed.notes.d.ts +0 -28
- package/dist/builtins/res/feed.notes.js +0 -24
- package/dist/builtins/res/feed.notes.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Builtin rmdir: removes empty folders, as on Linux.
|
|
3
|
+
*
|
|
4
|
+
* Inside a projection that holds folders a user makes (a tag under
|
|
5
|
+
* /proc/tags) the projection removes it, and a tag still worn by a feed is
|
|
6
|
+
* "Directory not empty". On the CUBE store a folder that holds anything is
|
|
7
|
+
* refused by name and an empty one is removed; a projection that removes
|
|
8
|
+
* no folders refuses by name.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { envelope_ok, envelope_error, errorStack, listCache_get } from '@fnndsc/cumin';
|
|
14
|
+
import { vfsDispatcher } from '@fnndsc/salsa';
|
|
15
|
+
import { path_resolve, error_stripDebugPrefix } from '../utils.js';
|
|
16
|
+
import { folder_checkExists } from './folderExists.js';
|
|
17
|
+
import { rm_run } from './rm.js';
|
|
18
|
+
/**
|
|
19
|
+
* Parses rmdir's arguments: paths only; any flag is refused by name.
|
|
20
|
+
*
|
|
21
|
+
* @param args - Raw argument tokens.
|
|
22
|
+
* @returns The paths, or the refusal.
|
|
23
|
+
*/
|
|
24
|
+
export function rmdirArgs_parse(args) {
|
|
25
|
+
const paths = [];
|
|
26
|
+
let endOfOptions = false;
|
|
27
|
+
for (const arg of args) {
|
|
28
|
+
if (endOfOptions || arg === '-' || !arg.startsWith('-')) {
|
|
29
|
+
paths.push(arg);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (arg === '--') {
|
|
33
|
+
endOfOptions = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
return { refused: arg.startsWith('--') ? `rmdir: unrecognized option '${arg}'` : `rmdir: invalid option -- '${arg.slice(1, 2)}'` };
|
|
37
|
+
}
|
|
38
|
+
return { paths };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The reason on the stack, worded as rmdir words it.
|
|
42
|
+
*
|
|
43
|
+
* @param pathArg - The operand as typed.
|
|
44
|
+
* @returns `rmdir: failed to remove '<path>': <reason>`.
|
|
45
|
+
*/
|
|
46
|
+
function reason_said(pathArg) {
|
|
47
|
+
const reason = errorStack.stack_pop();
|
|
48
|
+
const said = reason === undefined ? 'failed' : error_stripDebugPrefix(reason.message);
|
|
49
|
+
if (said.startsWith('rmdir:'))
|
|
50
|
+
return said;
|
|
51
|
+
return `rmdir: failed to remove '${pathArg}': ${said.replace(/^[^:]*: /, '')}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Removes one empty folder on the CUBE store: one that holds anything is
|
|
55
|
+
* refused, an empty one goes through rm's own removal.
|
|
56
|
+
*
|
|
57
|
+
* @param targetPath - The resolved path.
|
|
58
|
+
* @param pathArg - The operand as typed.
|
|
59
|
+
* @returns Null when removed, or the refusal's words.
|
|
60
|
+
*/
|
|
61
|
+
async function storeFolder_remove(targetPath, pathArg) {
|
|
62
|
+
if (!(await folder_checkExists(targetPath)))
|
|
63
|
+
return `rmdir: failed to remove '${pathArg}': No such file or directory`;
|
|
64
|
+
const held = await vfsDispatcher.list(targetPath);
|
|
65
|
+
if (!held.ok)
|
|
66
|
+
return reason_said(pathArg);
|
|
67
|
+
if (held.value.length > 0)
|
|
68
|
+
return `rmdir: failed to remove '${pathArg}': Directory not empty`;
|
|
69
|
+
const removed = await rm_run({ recursive: true, force: false, interactive: false, once: false, paths: [targetPath] });
|
|
70
|
+
return removed.status === 'ok' ? null : `rmdir: failed to remove '${pathArg}'`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Removes empty folders.
|
|
74
|
+
*
|
|
75
|
+
* @param args - The folders.
|
|
76
|
+
* @returns An envelope whose model lists each folder's outcome; silent on success.
|
|
77
|
+
*/
|
|
78
|
+
export async function builtin_rmdir(args) {
|
|
79
|
+
const parsed = rmdirArgs_parse(args);
|
|
80
|
+
if ('refused' in parsed)
|
|
81
|
+
return envelope_error('', undefined, `${chalk.red(parsed.refused)}\n`);
|
|
82
|
+
if (parsed.paths.length === 0)
|
|
83
|
+
return envelope_error('', undefined, `${chalk.red('Usage: rmdir <directory> [directory...]')}\n`);
|
|
84
|
+
let renderedErr = '';
|
|
85
|
+
const outcomes = [];
|
|
86
|
+
for (const pathArg of parsed.paths) {
|
|
87
|
+
const targetPath = await path_resolve(pathArg);
|
|
88
|
+
let refusal;
|
|
89
|
+
if (vfsDispatcher.path_isVirtual(targetPath)) {
|
|
90
|
+
refusal = (await vfsDispatcher.rmdir(targetPath)) ? null : reason_said(pathArg);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
refusal = await storeFolder_remove(targetPath, pathArg);
|
|
94
|
+
}
|
|
95
|
+
if (refusal !== null)
|
|
96
|
+
renderedErr += `${chalk.red(refusal)}\n`;
|
|
97
|
+
else
|
|
98
|
+
listCache_get().cache_invalidate(targetPath.slice(0, targetPath.lastIndexOf('/')) || '/');
|
|
99
|
+
outcomes.push({ path: targetPath, removed: refusal === null });
|
|
100
|
+
}
|
|
101
|
+
const model = { kind: 'fs.rmdir', data: outcomes };
|
|
102
|
+
if (renderedErr !== '') {
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
const envelope = envelope_error('', undefined, renderedErr);
|
|
105
|
+
envelope.model = model;
|
|
106
|
+
return envelope;
|
|
107
|
+
}
|
|
108
|
+
return envelope_ok('', model);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=rmdir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rmdir.js","sourceRoot":"","sources":["../../../src/builtins/fs/rmdir.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,14 +22,15 @@ export async function touch_run(runOptions) {
|
|
|
22
22
|
return envelope_error('', undefined, `${chalk.red('Usage: touch [--withContents <string>] [--withContentsFromFile <file>] <file>')}\n`);
|
|
23
23
|
}
|
|
24
24
|
const options = {};
|
|
25
|
-
|
|
25
|
+
// Empty text is text: `--withContents ''` writes an empty file.
|
|
26
|
+
if (runOptions.contents !== undefined) {
|
|
26
27
|
options.withContents = runOptions.contents;
|
|
27
28
|
}
|
|
28
29
|
if (runOptions.contentsFromFile) {
|
|
29
30
|
options.withContentsFromFile = runOptions.contentsFromFile;
|
|
30
31
|
}
|
|
31
32
|
// Only process the first file argument when injecting content
|
|
32
|
-
const filesToTouch = (options.withContents || options.withContentsFromFile)
|
|
33
|
+
const filesToTouch = (options.withContents !== undefined || options.withContentsFromFile)
|
|
33
34
|
? [pathArgs[0]]
|
|
34
35
|
: pathArgs;
|
|
35
36
|
let rendered = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"touch.js","sourceRoot":"","sources":["../../../src/builtins/fs/touch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAmB,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAExG,OAAO,EAAc,mBAAmB,EAAE,sBAAsB,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5H,OAAO,EAAE,WAAW,IAAI,eAAe,EAAgB,MAAM,oCAAoC,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAkBzD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAA2B;IACzD,MAAM,QAAQ,GAAa,UAAU,CAAC,KAAK,CAAC;IAE5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,cAAc,CACnB,EAAE,EACF,SAAS,EACT,GAAG,KAAK,CAAC,GAAG,CAAC,+EAA+E,CAAC,IAAI,CAClG,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"touch.js","sourceRoot":"","sources":["../../../src/builtins/fs/touch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** The one attribute a feed carries. */
|
|
2
|
+
export declare const XATTR_TAG: string;
|
|
3
|
+
/** How `setfattr` is spelled. */
|
|
4
|
+
export declare const SETFATTR_USAGE: string;
|
|
5
|
+
/** How `getfattr` is spelled. */
|
|
6
|
+
export declare const GETFATTR_USAGE: string;
|
|
7
|
+
/** A parsed `setfattr` invocation. */
|
|
8
|
+
export interface SetfattrArgs {
|
|
9
|
+
/** Set a value (`-n`), or remove (`-x`). */
|
|
10
|
+
mode: 'set' | 'remove';
|
|
11
|
+
/** The value to set or remove; null removes every value (`-x` alone). */
|
|
12
|
+
value: string | null;
|
|
13
|
+
paths: string[];
|
|
14
|
+
error: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** A parsed `getfattr` invocation. */
|
|
17
|
+
export interface GetfattrArgs {
|
|
18
|
+
paths: string[];
|
|
19
|
+
error: string | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reads a `setfattr` invocation. Unknown flags and attributes are refused by name.
|
|
23
|
+
*
|
|
24
|
+
* @param args - The tokens after the verb.
|
|
25
|
+
* @returns The parsed invocation, its error set when it is wrong.
|
|
26
|
+
*/
|
|
27
|
+
export declare function setfattrArgs_parse(args: ReadonlyArray<string>): SetfattrArgs;
|
|
28
|
+
/**
|
|
29
|
+
* Reads a `getfattr` invocation. `-n tag` is accepted (it is the only
|
|
30
|
+
* attribute); `-d` (dump all) reads the same.
|
|
31
|
+
*
|
|
32
|
+
* @param args - The tokens after the verb.
|
|
33
|
+
* @returns The parsed invocation, its error set when it is wrong.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getfattrArgs_parse(args: ReadonlyArray<string>): GetfattrArgs;
|
|
36
|
+
/**
|
|
37
|
+
* The feed a path names: an id, a `feed_N`, a path through `/feeds/` as
|
|
38
|
+
* setfacl reads it, or the projection `/proc/jobs/feed_N`.
|
|
39
|
+
*
|
|
40
|
+
* @param target - The path as typed.
|
|
41
|
+
* @returns The feed id, or null.
|
|
42
|
+
*/
|
|
43
|
+
export declare function xattrTarget_resolve(target: string): number | null;
|
|
44
|
+
/**
|
|
45
|
+
* Renders a feed's tags the way `getfattr` dumps attributes: a `# file:`
|
|
46
|
+
* line, then one `tag="…"` line per value; nothing under it when bare.
|
|
47
|
+
*
|
|
48
|
+
* @param path - The path as named.
|
|
49
|
+
* @param tags - The tag names.
|
|
50
|
+
* @returns The block.
|
|
51
|
+
*/
|
|
52
|
+
export declare function xattr_render(path: string, tags: ReadonlyArray<string>): string;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Shared `setfattr` / `getfattr` command-line grammar.
|
|
3
|
+
*
|
|
4
|
+
* A feed's tags are, in POSIX terms, an extended attribute: `setfattr -n
|
|
5
|
+
* tag -v urgent feed_12` hangs one, `getfattr feed_12` reads them. One
|
|
6
|
+
* attribute exists, `tag` (also spelled `user.tag`), and it holds many
|
|
7
|
+
* values — CUBE's tags on the feed.
|
|
8
|
+
*
|
|
9
|
+
* Dependency-free, as `acl.args.ts` is: the engine graph cannot be loaded
|
|
10
|
+
* under jest, so a grammar inside the builtin is a grammar nothing tests.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { aclTarget_resolve } from './acl.args.js';
|
|
15
|
+
/** The one attribute a feed carries. */
|
|
16
|
+
export const XATTR_TAG = 'tag';
|
|
17
|
+
/** How `setfattr` is spelled. */
|
|
18
|
+
export const SETFATTR_USAGE = 'setfattr: usage: setfattr -n tag -v <value> <path>... | setfattr -x tag [-v <value>] <path>...';
|
|
19
|
+
/** How `getfattr` is spelled. */
|
|
20
|
+
export const GETFATTR_USAGE = 'getfattr: usage: getfattr [-n tag] <path>...';
|
|
21
|
+
/**
|
|
22
|
+
* Whether an attribute name is the one a feed carries.
|
|
23
|
+
*
|
|
24
|
+
* @param name - The name as typed.
|
|
25
|
+
* @returns True for `tag` and `user.tag`.
|
|
26
|
+
*/
|
|
27
|
+
function name_isTag(name) {
|
|
28
|
+
return name === XATTR_TAG || name === `user.${XATTR_TAG}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Reads a `setfattr` invocation. Unknown flags and attributes are refused by name.
|
|
32
|
+
*
|
|
33
|
+
* @param args - The tokens after the verb.
|
|
34
|
+
* @returns The parsed invocation, its error set when it is wrong.
|
|
35
|
+
*/
|
|
36
|
+
export function setfattrArgs_parse(args) {
|
|
37
|
+
let mode = null;
|
|
38
|
+
let name = null;
|
|
39
|
+
let value = null;
|
|
40
|
+
const paths = [];
|
|
41
|
+
const fail = (error) => ({ mode: 'set', value: null, paths: [], error });
|
|
42
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
43
|
+
const token = args[i] ?? '';
|
|
44
|
+
if (token === '-n' || token === '--name' || token === '-x' || token === '--remove') {
|
|
45
|
+
const given = args[i + 1];
|
|
46
|
+
if (given === undefined)
|
|
47
|
+
return fail(`setfattr: option '${token}' requires an argument\n${SETFATTR_USAGE}`);
|
|
48
|
+
mode = token === '-n' || token === '--name' ? 'set' : 'remove';
|
|
49
|
+
name = given;
|
|
50
|
+
i += 1;
|
|
51
|
+
}
|
|
52
|
+
else if (token === '-v' || token === '--value') {
|
|
53
|
+
const given = args[i + 1];
|
|
54
|
+
if (given === undefined)
|
|
55
|
+
return fail(`setfattr: option '${token}' requires an argument\n${SETFATTR_USAGE}`);
|
|
56
|
+
value = given;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
else if (token.startsWith('-')) {
|
|
60
|
+
return fail(`setfattr: invalid option -- '${token.replace(/^-+/, '')}'\n${SETFATTR_USAGE}`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
paths.push(token);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (mode === null || name === null)
|
|
67
|
+
return fail(SETFATTR_USAGE);
|
|
68
|
+
if (!name_isTag(name))
|
|
69
|
+
return fail(`setfattr: ${name}: Operation not supported (a feed carries one attribute, tag)`);
|
|
70
|
+
if (mode === 'set' && (value === null || value.trim() === ''))
|
|
71
|
+
return fail(`setfattr: -n ${name} needs -v <value>\n${SETFATTR_USAGE}`);
|
|
72
|
+
if (paths.length === 0)
|
|
73
|
+
return fail(SETFATTR_USAGE);
|
|
74
|
+
return { mode, value: value === null ? null : value.trim(), paths, error: null };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Reads a `getfattr` invocation. `-n tag` is accepted (it is the only
|
|
78
|
+
* attribute); `-d` (dump all) reads the same.
|
|
79
|
+
*
|
|
80
|
+
* @param args - The tokens after the verb.
|
|
81
|
+
* @returns The parsed invocation, its error set when it is wrong.
|
|
82
|
+
*/
|
|
83
|
+
export function getfattrArgs_parse(args) {
|
|
84
|
+
const paths = [];
|
|
85
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
86
|
+
const token = args[i] ?? '';
|
|
87
|
+
if (token === '-n' || token === '--name') {
|
|
88
|
+
const given = args[i + 1];
|
|
89
|
+
if (given === undefined)
|
|
90
|
+
return { paths: [], error: `getfattr: option '${token}' requires an argument\n${GETFATTR_USAGE}` };
|
|
91
|
+
if (!name_isTag(given))
|
|
92
|
+
return { paths: [], error: `getfattr: ${given}: No such attribute (a feed carries one attribute, tag)` };
|
|
93
|
+
i += 1;
|
|
94
|
+
}
|
|
95
|
+
else if (token === '-d' || token === '--dump') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
else if (token.startsWith('-')) {
|
|
99
|
+
return { paths: [], error: `getfattr: invalid option -- '${token.replace(/^-+/, '')}'\n${GETFATTR_USAGE}` };
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
paths.push(token);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (paths.length === 0)
|
|
106
|
+
return { paths: [], error: GETFATTR_USAGE };
|
|
107
|
+
return { paths, error: null };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The feed a path names: an id, a `feed_N`, a path through `/feeds/` as
|
|
111
|
+
* setfacl reads it, or the projection `/proc/jobs/feed_N`.
|
|
112
|
+
*
|
|
113
|
+
* @param target - The path as typed.
|
|
114
|
+
* @returns The feed id, or null.
|
|
115
|
+
*/
|
|
116
|
+
export function xattrTarget_resolve(target) {
|
|
117
|
+
const held = aclTarget_resolve(target);
|
|
118
|
+
if (held !== null)
|
|
119
|
+
return held;
|
|
120
|
+
const projected = target.match(/\/jobs\/feed_(\d+)(?:\/|$)/);
|
|
121
|
+
return projected !== null ? Number(projected[1]) : null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Renders a feed's tags the way `getfattr` dumps attributes: a `# file:`
|
|
125
|
+
* line, then one `tag="…"` line per value; nothing under it when bare.
|
|
126
|
+
*
|
|
127
|
+
* @param path - The path as named.
|
|
128
|
+
* @param tags - The tag names.
|
|
129
|
+
* @returns The block.
|
|
130
|
+
*/
|
|
131
|
+
export function xattr_render(path, tags) {
|
|
132
|
+
return [`# file: ${path}`, ...tags.map((tag) => `${XATTR_TAG}="${tag.replace(/"/g, '\\"')}"`)].join('\n');
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=xattr.args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xattr.args.js","sourceRoot":"","sources":["../../../src/builtins/fs/xattr.args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `setfattr` and `getfattr` — a feed's tags.
|
|
3
|
+
*
|
|
4
|
+
* CUBE's freeform tags on a feed are, in POSIX terms, the values of one
|
|
5
|
+
* extended attribute, `tag`. The capability lives in the kernel
|
|
6
|
+
* (`feedTags_list`, `feedTag_add`, `feedTag_remove`); this is its shell face.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { CommandEnvelope } from '@fnndsc/cumin';
|
|
11
|
+
/**
|
|
12
|
+
* Reads the tags a feed wears, in `getfattr`'s dump shape.
|
|
13
|
+
*
|
|
14
|
+
* @param args - Raw argument tokens.
|
|
15
|
+
* @returns An envelope carrying the rendered blocks and a model of them.
|
|
16
|
+
*/
|
|
17
|
+
export declare function builtin_getfattr(args: string[]): Promise<CommandEnvelope>;
|
|
18
|
+
/**
|
|
19
|
+
* Hangs a tag on feeds (`-n tag -v <value>`), or takes one off (`-x tag -v
|
|
20
|
+
* <value>`), or takes every tag off (`-x tag`, as setfattr removes the
|
|
21
|
+
* whole attribute). Silent on success, as setfattr is; a refusal is named.
|
|
22
|
+
*
|
|
23
|
+
* @param args - Raw argument tokens.
|
|
24
|
+
* @returns An envelope, empty on success.
|
|
25
|
+
*/
|
|
26
|
+
export declare function builtin_setfattr(args: string[]): Promise<CommandEnvelope>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `setfattr` and `getfattr` — a feed's tags.
|
|
3
|
+
*
|
|
4
|
+
* CUBE's freeform tags on a feed are, in POSIX terms, the values of one
|
|
5
|
+
* extended attribute, `tag`. The capability lives in the kernel
|
|
6
|
+
* (`feedTags_list`, `feedTag_add`, `feedTag_remove`); this is its shell face.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { envelope_ok, envelope_error, errorStack, feedTags_list, feedTag_add, feedTag_remove, } from '@fnndsc/cumin';
|
|
11
|
+
import { getfattrArgs_parse, setfattrArgs_parse, xattrTarget_resolve, xattr_render, } from './xattr.args.js';
|
|
12
|
+
import { error_stripDebugPrefix } from '../utils.js';
|
|
13
|
+
/**
|
|
14
|
+
* A refusal, on a line of its own.
|
|
15
|
+
*
|
|
16
|
+
* @param message - What was refused, and why.
|
|
17
|
+
* @returns The error envelope.
|
|
18
|
+
*/
|
|
19
|
+
function refusal(message) {
|
|
20
|
+
return envelope_error(`${message}\n`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The kernel's last word, or a fallback.
|
|
24
|
+
*
|
|
25
|
+
* @param fallback - What to say when the kernel said nothing.
|
|
26
|
+
* @returns The message.
|
|
27
|
+
*/
|
|
28
|
+
function error_said(fallback) {
|
|
29
|
+
const error = errorStack.stack_pop();
|
|
30
|
+
return error?.message ?? fallback;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reads the tags a feed wears, in `getfattr`'s dump shape.
|
|
34
|
+
*
|
|
35
|
+
* @param args - Raw argument tokens.
|
|
36
|
+
* @returns An envelope carrying the rendered blocks and a model of them.
|
|
37
|
+
*/
|
|
38
|
+
export async function builtin_getfattr(args) {
|
|
39
|
+
const parsed = getfattrArgs_parse(args);
|
|
40
|
+
if (parsed.error !== null)
|
|
41
|
+
return refusal(parsed.error);
|
|
42
|
+
const blocks = [];
|
|
43
|
+
const model = [];
|
|
44
|
+
for (const path of parsed.paths) {
|
|
45
|
+
const feedId = xattrTarget_resolve(path);
|
|
46
|
+
if (feedId === null)
|
|
47
|
+
return refusal(`getfattr: '${path}' does not name a feed`);
|
|
48
|
+
const held = await feedTags_list(feedId);
|
|
49
|
+
if (!held.ok)
|
|
50
|
+
return refusal(error_said(`getfattr: could not read the tags of feed ${feedId}`));
|
|
51
|
+
const tags = held.value.map((tag) => tag.name);
|
|
52
|
+
blocks.push(xattr_render(path, tags));
|
|
53
|
+
model.push({ path, feedId, tags });
|
|
54
|
+
}
|
|
55
|
+
return envelope_ok(`${blocks.join('\n\n')}\n`, { kind: 'fs.xattr', data: model });
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Hangs a tag on feeds (`-n tag -v <value>`), or takes one off (`-x tag -v
|
|
59
|
+
* <value>`), or takes every tag off (`-x tag`, as setfattr removes the
|
|
60
|
+
* whole attribute). Silent on success, as setfattr is; a refusal is named.
|
|
61
|
+
*
|
|
62
|
+
* @param args - Raw argument tokens.
|
|
63
|
+
* @returns An envelope, empty on success.
|
|
64
|
+
*/
|
|
65
|
+
export async function builtin_setfattr(args) {
|
|
66
|
+
const parsed = setfattrArgs_parse(args);
|
|
67
|
+
if (parsed.error !== null)
|
|
68
|
+
return refusal(parsed.error);
|
|
69
|
+
const changed = [];
|
|
70
|
+
for (const path of parsed.paths) {
|
|
71
|
+
const feedId = xattrTarget_resolve(path);
|
|
72
|
+
if (feedId === null)
|
|
73
|
+
return refusal(`setfattr: '${path}' does not name a feed`);
|
|
74
|
+
let names;
|
|
75
|
+
if (parsed.mode === 'remove' && parsed.value === null) {
|
|
76
|
+
const held = await feedTags_list(feedId);
|
|
77
|
+
if (!held.ok)
|
|
78
|
+
return refusal(error_said(`setfattr: could not read the tags of feed ${feedId}`));
|
|
79
|
+
names = held.value.map((tag) => tag.name);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
names = [parsed.value ?? ''];
|
|
83
|
+
}
|
|
84
|
+
for (const name of names) {
|
|
85
|
+
const done = parsed.mode === 'set' ? await feedTag_add(feedId, name) : await feedTag_remove(feedId, name);
|
|
86
|
+
if (!done.ok) {
|
|
87
|
+
const said = error_stripDebugPrefix(error_said(`could not ${parsed.mode === 'set' ? 'add' : 'remove'} tag ${name}`));
|
|
88
|
+
return refusal(`setfattr: ${path}: ${said}`);
|
|
89
|
+
}
|
|
90
|
+
if (parsed.mode === 'remove' && parsed.value !== null && !done.value) {
|
|
91
|
+
return refusal(`setfattr: ${path}: tag="${name}": No such attribute value`);
|
|
92
|
+
}
|
|
93
|
+
changed.push({ feedId, tag: name, done: done.value });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return envelope_ok('', { kind: 'fs.xattr.changed', data: { mode: parsed.mode, changed } });
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=xattr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xattr.js","sourceRoot":"","sources":["../../../src/builtins/fs/xattr.ts"],"names":[],"mappings":"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"}
|