@fnndsc/brasa 0.56.1 → 0.56.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.
Files changed (98) hide show
  1. package/dist/buildinfo.json +1 -1
  2. package/dist/builtins/berth/berth.d.ts +90 -3
  3. package/dist/builtins/berth/berth.js +129 -76
  4. package/dist/builtins/berth/berth.js.map +1 -1
  5. package/dist/builtins/berth/berthClose.d.ts +25 -6
  6. package/dist/builtins/berth/berthClose.js +8 -64
  7. package/dist/builtins/berth/berthClose.js.map +1 -1
  8. package/dist/builtins/berth/berthCube.js +4 -6
  9. package/dist/builtins/berth/berthCube.js.map +1 -1
  10. package/dist/builtins/berth/berthProcesses.js +33 -3
  11. package/dist/builtins/berth/berthProcesses.js.map +1 -1
  12. package/dist/builtins/fs/cat.js +3 -3
  13. package/dist/builtins/fs/cat.js.map +1 -1
  14. package/dist/builtins/fs/cp.js +3 -2
  15. package/dist/builtins/fs/cp.js.map +1 -1
  16. package/dist/builtins/fs/download.d.ts +31 -4
  17. package/dist/builtins/fs/download.js +75 -38
  18. package/dist/builtins/fs/download.js.map +1 -1
  19. package/dist/builtins/fs/ls.d.ts +69 -4
  20. package/dist/builtins/fs/ls.js +84 -45
  21. package/dist/builtins/fs/ls.js.map +1 -1
  22. package/dist/builtins/fs/pull.js +37 -9
  23. package/dist/builtins/fs/pull.js.map +1 -1
  24. package/dist/builtins/help.d.ts +3 -1
  25. package/dist/builtins/help.js +22 -33
  26. package/dist/builtins/help.js.map +1 -1
  27. package/dist/builtins/res/dicom.d.ts +3 -4
  28. package/dist/builtins/res/dicom.js +5 -19
  29. package/dist/builtins/res/dicom.js.map +1 -1
  30. package/dist/builtins/sys/textFilters.js +2 -2
  31. package/dist/builtins/sys/textFilters.js.map +1 -1
  32. package/dist/builtins/utils.d.ts +1 -1
  33. package/dist/builtins/utils.js +1 -1
  34. package/dist/builtins/utils.js.map +1 -1
  35. package/dist/chris/backend.js +2 -0
  36. package/dist/chris/backend.js.map +1 -1
  37. package/dist/chris/chrisCommands.js +6 -9
  38. package/dist/chris/chrisCommands.js.map +1 -1
  39. package/dist/chris/files.js +24 -85
  40. package/dist/chris/files.js.map +1 -1
  41. package/dist/chris/help.js +0 -64
  42. package/dist/chris/help.js.map +1 -1
  43. package/dist/chris/kinds.d.ts +9 -0
  44. package/dist/chris/kinds.js +42 -0
  45. package/dist/chris/kinds.js.map +1 -0
  46. package/dist/chris/promptContext.d.ts +6 -0
  47. package/dist/chris/promptContext.js +3 -1
  48. package/dist/chris/promptContext.js.map +1 -1
  49. package/dist/core/argumentKinds.d.ts +107 -0
  50. package/dist/core/argumentKinds.js +223 -0
  51. package/dist/core/argumentKinds.js.map +1 -0
  52. package/dist/core/backend.d.ts +4 -1
  53. package/dist/core/backend.js.map +1 -1
  54. package/dist/core/commandDeclaration.d.ts +363 -0
  55. package/dist/core/commandDeclaration.js +31 -0
  56. package/dist/core/commandDeclaration.js.map +1 -0
  57. package/dist/core/commandParse.d.ts +64 -0
  58. package/dist/core/commandParse.js +420 -0
  59. package/dist/core/commandParse.js.map +1 -0
  60. package/dist/core/commandRegistry.d.ts +36 -0
  61. package/dist/core/commandRegistry.js +72 -3
  62. package/dist/core/commandRegistry.js.map +1 -1
  63. package/dist/core/commandSchema.d.ts +32 -0
  64. package/dist/core/commandSchema.js +114 -0
  65. package/dist/core/commandSchema.js.map +1 -0
  66. package/dist/core/commandUsage.d.ts +47 -0
  67. package/dist/core/commandUsage.js +195 -0
  68. package/dist/core/commandUsage.js.map +1 -0
  69. package/dist/core/coreCommands.js +5 -4
  70. package/dist/core/coreCommands.js.map +1 -1
  71. package/dist/core/dispatch.js +15 -8
  72. package/dist/core/dispatch.js.map +1 -1
  73. package/dist/core/paths.d.ts +23 -0
  74. package/dist/core/paths.js +73 -2
  75. package/dist/core/paths.js.map +1 -1
  76. package/dist/core/preprocess.d.ts +10 -8
  77. package/dist/core/preprocess.js +11 -20
  78. package/dist/core/preprocess.js.map +1 -1
  79. package/dist/core/warmupFailures.d.ts +34 -2
  80. package/dist/core/warmupFailures.js +43 -2
  81. package/dist/core/warmupFailures.js.map +1 -1
  82. package/dist/lib/completer/cfsPaths.d.ts +14 -0
  83. package/dist/lib/completer/cfsPaths.js +91 -0
  84. package/dist/lib/completer/cfsPaths.js.map +1 -0
  85. package/dist/lib/completer/declared.d.ts +15 -0
  86. package/dist/lib/completer/declared.js +186 -0
  87. package/dist/lib/completer/declared.js.map +1 -0
  88. package/dist/lib/completer/index.js +33 -74
  89. package/dist/lib/completer/index.js.map +1 -1
  90. package/dist/lib/parser.d.ts +5 -0
  91. package/dist/lib/parser.js +2 -0
  92. package/dist/lib/parser.js.map +1 -1
  93. package/dist/lib/vfs/vfs.js +4 -2
  94. package/dist/lib/vfs/vfs.js.map +1 -1
  95. package/dist/lib/wildcard.js +2 -3
  96. package/dist/lib/wildcard.js.map +1 -1
  97. package/dist/notes.json +1 -1
  98. package/package.json +5 -4
@@ -0,0 +1,223 @@
1
+ import { dirname, join, resolve } from 'node:path';
2
+ import { z } from 'zod';
3
+ import { backendInstalled_get } from './backend.js';
4
+ /** The core's kind names, which a core command may use and no other. */
5
+ export const CORE_KIND_NAMES = ['word', 'enum', 'int', 'number', 'cfsPath', 'hostPath', 'variable', 'job'];
6
+ /** A variable's name, as `set` and `${name}` spell it. */
7
+ const VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
8
+ /** A joblet as a word names it: `%3`, `3`, `%%`, `%+`, `%`. */
9
+ const JOB_WORD = /^(?:%?\d+|%%|%\+|%)$/;
10
+ /** A whole number as a word writes it. */
11
+ const INT_WORD = /^[+-]?\d+$/;
12
+ /**
13
+ * Checks a number against an argument's bounds.
14
+ *
15
+ * @param value - The number.
16
+ * @param argument - The argument, with its bounds.
17
+ * @returns The value, or why it is out of bounds.
18
+ */
19
+ function bounds_check(value, argument) {
20
+ if (argument.min !== undefined && value < argument.min)
21
+ return { ok: false, reason: `at least ${argument.min}` };
22
+ if (argument.max !== undefined && value > argument.max)
23
+ return { ok: false, reason: `at most ${argument.max}` };
24
+ return { ok: true, value };
25
+ }
26
+ /**
27
+ * A number schema with an argument's bounds.
28
+ *
29
+ * @param base - The number schema.
30
+ * @param argument - The argument, with its bounds.
31
+ * @returns The schema, bounded.
32
+ */
33
+ function numberSchema_bound(base, argument) {
34
+ let schema = base;
35
+ if (argument.min !== undefined)
36
+ schema = schema.min(argument.min);
37
+ if (argument.max !== undefined)
38
+ schema = schema.max(argument.max);
39
+ return schema;
40
+ }
41
+ /**
42
+ * Reads a word as text that is not empty.
43
+ *
44
+ * @param word - The word.
45
+ * @param what - What it should be, for the reason.
46
+ * @returns The word, or why it is not one.
47
+ */
48
+ function text_read(word, what) {
49
+ return word === '' ? { ok: false, reason: `an empty word is not ${what}` } : { ok: true, value: word };
50
+ }
51
+ /**
52
+ * The candidates among some words that start with what was typed.
53
+ *
54
+ * @param words - The words.
55
+ * @param partial - What was typed.
56
+ * @returns Those that start with it.
57
+ */
58
+ export function candidates_filter(words, partial) {
59
+ return words.filter((word) => word.startsWith(partial));
60
+ }
61
+ /** Any word at all. */
62
+ const WORD_KIND = {
63
+ name: 'word',
64
+ summary: 'a word',
65
+ word_read: (word) => ({ ok: true, value: word }),
66
+ schema_make: () => z.string(),
67
+ };
68
+ /** One of the words an argument lists. */
69
+ const ENUM_KIND = {
70
+ name: 'enum',
71
+ summary: 'one of the listed words',
72
+ word_read: (word, argument) => {
73
+ const values = argument.values ?? [];
74
+ return values.includes(word) ? { ok: true, value: word } : { ok: false, reason: `valid: ${values.join(', ')}` };
75
+ },
76
+ schema_make: (argument) => z.enum([...(argument.values ?? [])]),
77
+ words_complete: async (partial, argument) => candidates_filter(argument.values ?? [], partial),
78
+ };
79
+ /** A whole number. */
80
+ const INT_KIND = {
81
+ name: 'int',
82
+ summary: 'a whole number',
83
+ numeric: true,
84
+ word_read: (word, argument) => INT_WORD.test(word) ? bounds_check(Number(word), argument) : { ok: false, reason: 'not a whole number' },
85
+ schema_make: (argument) => numberSchema_bound(z.number().int(), argument),
86
+ };
87
+ /** A number, whole or not. */
88
+ const NUMBER_KIND = {
89
+ name: 'number',
90
+ summary: 'a number',
91
+ numeric: true,
92
+ word_read: (word, argument) => {
93
+ const value = word.trim() === '' ? Number.NaN : Number(word);
94
+ return Number.isFinite(value) ? bounds_check(value, argument) : { ok: false, reason: 'not a number' };
95
+ },
96
+ schema_make: (argument) => numberSchema_bound(z.number(), argument),
97
+ };
98
+ /** A path in ChRIS's filesystem (CFS), as the session resolves it. */
99
+ const CFS_PATH_KIND = {
100
+ name: 'cfsPath',
101
+ summary: "a path in ChRIS's filesystem",
102
+ path: true,
103
+ word_read: (word) => text_read(word, 'a path'),
104
+ schema_make: () => z.string().min(1),
105
+ words_complete: async (partial) => {
106
+ const { cfsPaths_complete } = await import('../lib/completer/cfsPaths.js');
107
+ return cfsPaths_complete(partial);
108
+ },
109
+ words_expand: async (words) => {
110
+ const { shellWords_expand } = await import('../lib/wildcard.js');
111
+ const { errorStack } = await import('@fnndsc/fond');
112
+ const expanded = await shellWords_expand(words, () => true);
113
+ if (expanded.ok)
114
+ return { ok: true, words: expanded.value };
115
+ const reason = errorStack.stack_pop()?.message;
116
+ return { ok: false, reason: reason ?? 'a wildcard could not be expanded' };
117
+ },
118
+ };
119
+ /**
120
+ * The folders and files under a host folder that start with a name, for
121
+ * completion of a host path.
122
+ *
123
+ * The disk is loaded when asked, so parsing a line loads no filesystem.
124
+ *
125
+ * @param partial - The path typed so far.
126
+ * @returns The candidates, a folder ending in `/`; none when the folder cannot be read.
127
+ */
128
+ async function hostPaths_list(partial) {
129
+ const { readdirSync } = await import('node:fs');
130
+ const { homedir } = await import('node:os');
131
+ const expanded = partial.startsWith('~/') ? join(homedir(), partial.slice(2)) : partial;
132
+ const folder = expanded.endsWith('/') ? expanded : dirname(expanded);
133
+ const typedFolder = partial.endsWith('/') ? partial : partial.slice(0, partial.lastIndexOf('/') + 1);
134
+ const prefix = expanded.endsWith('/') ? '' : expanded.slice(expanded.lastIndexOf('/') + 1);
135
+ let entries = [];
136
+ try {
137
+ entries = readdirSync(resolve(folder === '' ? '.' : folder), { withFileTypes: true });
138
+ }
139
+ catch {
140
+ return [];
141
+ }
142
+ return entries
143
+ .filter((entry) => entry.name.startsWith(prefix))
144
+ .map((entry) => `${typedFolder}${entry.name}${entry.isDirectory() ? '/' : ''}`)
145
+ .sort();
146
+ }
147
+ /**
148
+ * A path on the host the operator's surface sits on. It completes from the
149
+ * engine's own disk only when that disk is the surface's: under a daemon the
150
+ * engine's disk is a machine nobody at the surface sits at.
151
+ */
152
+ const HOST_PATH_KIND = {
153
+ name: 'hostPath',
154
+ summary: "a path on this machine's filesystem",
155
+ path: true,
156
+ word_read: (word) => text_read(word, 'a path'),
157
+ schema_make: () => z.string().min(1),
158
+ words_complete: async (partial) => {
159
+ const { surface_get } = await import('./surface.js');
160
+ return surface_get().capabilities.engineFilesystem ? hostPaths_list(partial) : [];
161
+ },
162
+ };
163
+ /** A session variable's name. */
164
+ const VARIABLE_KIND = {
165
+ name: 'variable',
166
+ summary: 'a variable name',
167
+ word_read: (word) => VARIABLE_NAME.test(word) ? { ok: true, value: word } : { ok: false, reason: 'not a variable name' },
168
+ schema_make: () => z.string().regex(VARIABLE_NAME),
169
+ words_complete: async (partial) => {
170
+ const { variables_list } = await import('./variables.js');
171
+ return candidates_filter(variables_list().map((variable) => variable.name), partial);
172
+ },
173
+ };
174
+ /** A joblet: `%3`, `3`, or the most recent, `%%`. */
175
+ const JOB_KIND = {
176
+ name: 'job',
177
+ summary: 'a job: %N, or %% for the most recent',
178
+ word_read: (word) => JOB_WORD.test(word) ? { ok: true, value: word } : { ok: false, reason: 'a job is %N, or %% for the most recent' },
179
+ schema_make: () => z.string().regex(JOB_WORD),
180
+ words_complete: async (partial) => {
181
+ const { joblets_running } = await import('./joblets.js');
182
+ return candidates_filter(joblets_running().map((joblet) => `%${joblet.number}`), partial);
183
+ },
184
+ };
185
+ /** The core's kinds, by name. */
186
+ const CORE_KINDS = new Map([WORD_KIND, ENUM_KIND, INT_KIND, NUMBER_KIND, CFS_PATH_KIND, HOST_PATH_KIND, VARIABLE_KIND, JOB_KIND]
187
+ .map((kind) => [kind.name, kind]));
188
+ /**
189
+ * Finds a kind: the core's, then the installed backend's.
190
+ *
191
+ * @param name - The kind's name.
192
+ * @returns The kind, or null when neither has it.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * argumentKind_get('cfsPath')?.path; // true
197
+ * ```
198
+ */
199
+ export function argumentKind_get(name) {
200
+ const core = CORE_KINDS.get(name);
201
+ if (core !== undefined)
202
+ return core;
203
+ return backendInstalled_get()?.argumentKinds?.find((kind) => kind.name === name) ?? null;
204
+ }
205
+ /**
206
+ * Checks one word against its argument's kind.
207
+ *
208
+ * @param argument - The argument's grammar.
209
+ * @param word - The word.
210
+ * @returns The value, or why the word is not one (an unregistered kind says so).
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * argumentWord_check({ kind: 'enum', values: ['a', 'b'] }, 'c'); // { ok: false, reason: 'valid: a, b' }
215
+ * ```
216
+ */
217
+ export function argumentWord_check(argument, word) {
218
+ const kind = argumentKind_get(argument.kind);
219
+ if (kind === null)
220
+ return { ok: false, reason: `this session has no argument kind ${argument.kind}` };
221
+ return kind.word_read(word, argument);
222
+ }
223
+ //# sourceMappingURL=argumentKinds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argumentKinds.js","sourceRoot":"","sources":["../../src/core/argumentKinds.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAkEpD,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AAElI,0DAA0D;AAC1D,MAAM,aAAa,GAAW,0BAA0B,CAAC;AAEzD,+DAA+D;AAC/D,MAAM,QAAQ,GAAW,sBAAsB,CAAC;AAEhD,0CAA0C;AAC1C,MAAM,QAAQ,GAAW,YAAY,CAAC;AAEtC;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,KAAa,EAAE,QAAyB;IAC5D,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC;IACjH,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC;IAChH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,IAAiB,EAAE,QAAyB;IACtE,IAAI,MAAM,GAAgB,IAAI,CAAC;IAC/B,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS;QAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAClE,IAAI,QAAQ,CAAC,GAAG,KAAK,SAAS;QAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,IAAY,EAAE,IAAY;IAC3C,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA4B,EAAE,OAAe;IAC7E,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,uBAAuB;AACvB,MAAM,SAAS,GAAiB;IAC9B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACvE,WAAW,EAAE,GAAc,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE;CACzC,CAAC;AAEF,0CAA0C;AAC1C,MAAM,SAAS,GAAiB;IAC9B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,yBAAyB;IAClC,SAAS,EAAE,CAAC,IAAY,EAAE,QAAyB,EAAiB,EAAE;QACpE,MAAM,MAAM,GAAsB,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IAClH,CAAC;IACD,WAAW,EAAE,CAAC,QAAyB,EAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAA0B,CAAC;IACpH,cAAc,EAAE,KAAK,EAAE,OAAe,EAAE,QAAyB,EAAqB,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC;CAC3I,CAAC;AAEF,sBAAsB;AACtB,MAAM,QAAQ,GAAiB;IAC7B,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,gBAAgB;IACzB,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,CAAC,IAAY,EAAE,QAAyB,EAAiB,EAAE,CACpE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE;IAC1G,WAAW,EAAE,CAAC,QAAyB,EAAa,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC;CACtG,CAAC;AAEF,8BAA8B;AAC9B,MAAM,WAAW,GAAiB;IAChC,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,CAAC,IAAY,EAAE,QAAyB,EAAiB,EAAE;QACpE,MAAM,KAAK,GAAW,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IACxG,CAAC;IACD,WAAW,EAAE,CAAC,QAAyB,EAAa,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC;CAChG,CAAC;AAEF,sEAAsE;AACtE,MAAM,aAAa,GAAiB;IAClC,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,8BAA8B;IACvC,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC;IACrE,WAAW,EAAE,GAAc,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,cAAc,EAAE,KAAK,EAAE,OAAe,EAAqB,EAAE;QAC3D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;QAC3E,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IACD,YAAY,EAAE,KAAK,EAAE,KAA2B,EAA8B,EAAE;QAC9E,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACjE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,GAAY,EAAE,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,QAAQ,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAuB,UAAU,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC;QACnE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,kCAAkC,EAAE,CAAC;IAC7E,CAAC;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,KAAK,UAAU,cAAc,CAAC,OAAe;IAC3C,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAW,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChG,MAAM,MAAM,GAAW,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAW,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7G,MAAM,MAAM,GAAW,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACnG,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;SACjE,GAAG,CAAC,CAAC,KAAa,EAAU,EAAE,CAAC,GAAG,WAAW,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SAC9F,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,cAAc,GAAiB;IACnC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,qCAAqC;IAC9C,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC;IACrE,WAAW,EAAE,GAAc,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,cAAc,EAAE,KAAK,EAAE,OAAe,EAAqB,EAAE;QAC3D,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACrD,OAAO,WAAW,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,CAAC;CACF,CAAC;AAEF,iCAAiC;AACjC,MAAM,aAAa,GAAiB;IAClC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,iBAAiB;IAC1B,SAAS,EAAE,CAAC,IAAY,EAAiB,EAAE,CACzC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE;IACrG,WAAW,EAAE,GAAc,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC;IAC7D,cAAc,EAAE,KAAK,EAAE,OAAe,EAAqB,EAAE;QAC3D,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC1D,OAAO,iBAAiB,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC,QAA0B,EAAU,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACjH,CAAC;CACF,CAAC;AAEF,qDAAqD;AACrD,MAAM,QAAQ,GAAiB;IAC7B,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,sCAAsC;IAC/C,SAAS,EAAE,CAAC,IAAY,EAAiB,EAAE,CACzC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,EAAE;IACnH,WAAW,EAAE,GAAc,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;IACxD,cAAc,EAAE,KAAK,EAAE,OAAe,EAAqB,EAAE;QAC3D,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACzD,OAAO,iBAAiB,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,MAA0B,EAAU,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IACxH,CAAC;CACF,CAAC;AAEF,iCAAiC;AACjC,MAAM,UAAU,GAAsC,IAAI,GAAG,CAC3D,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,CAAC;KAClG,GAAG,CAAC,CAAC,IAAkB,EAA0B,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAC1E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,IAAI,GAA6B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,oBAAoB,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,IAAkB,EAAW,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AAClH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAyB,EAAE,IAAY;IACxE,MAAM,IAAI,GAAwB,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClE,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IACtG,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC"}
@@ -17,6 +17,7 @@ import type { PromptContext, CubeTelemetry, JobsStateTelemetry, CommandEnvelope,
17
17
  import type { ReferenceValue } from '../lib/parser.js';
18
18
  import type { ListingLook, VFSDispatcher } from '@fnndsc/fond';
19
19
  import type { ListingItem } from '@fnndsc/menu';
20
+ import type { ArgumentKind } from './argumentKinds.js';
20
21
  /**
21
22
  * Who a session is and where: the input a host keys the session by (calypso's
22
23
  * berth name is `user@where`, normalised).
@@ -212,7 +213,7 @@ export interface BackendFilesystem {
212
213
  * cleared): a listing that fails says the path was walked as written.
213
214
  */
214
215
  readonly unreadLinks_take?: () => Promise<string[]>;
215
- /** A folder as the filesystem holds it, links followed, for globbing beneath it; absent, the path as given. */
216
+ /** A store path with the store's own links followed, at any depth (ChRIS: `.chrislink`s); absent, the path as given. `storePath_resolve` asks it for every read and listing, and globbing for a folder. */
216
217
  readonly path_physical?: (path: string) => Promise<string>;
217
218
  /** Paths that are always folders, entered without asking (beside the mounts and the folders above them). */
218
219
  readonly structural?: ReadonlyArray<string>;
@@ -321,6 +322,8 @@ export interface Backend {
321
322
  readonly vfs?: BackendFilesystem;
322
323
  /** What it adds to completion. */
323
324
  readonly completion?: BackendCompletion;
325
+ /** The argument kinds its commands declare beside the core's (ChRIS: an anchor's name); a kind is found among the core's first. */
326
+ readonly argumentKinds?: ReadonlyArray<ArgumentKind>;
324
327
  /** How its listings show: the kinds it lists beside the core's, and how a name is coloured. */
325
328
  readonly listingLook?: ListingLook;
326
329
  /** Whether the backend is debugging (error messages keep their function stamp), or null before it can say. */
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/core/backend.ts"],"names":[],"mappings":"AA4VA,IAAI,SAAS,GAAmB,IAAI,CAAC;AAErC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,SAAS,GAAG,OAAO,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;IAC3G,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/core/backend.ts"],"names":[],"mappings":"AA+VA,IAAI,SAAS,GAAmB,IAAI,CAAC;AAErC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,SAAS,GAAG,OAAO,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;IAC3G,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,363 @@
1
+ /**
2
+ * @file A command's declaration: its name, the grammar its line is parsed
3
+ * against, the kind of every argument, its help prose, what it holds of the
4
+ * session, and its handler, in one record.
5
+ *
6
+ * The grammar is the shell's own: subcommands; operands in order, each
7
+ * optional or repeated; options with a short name, a long name, or both, and
8
+ * a value or none. Every argument names a kind (`argumentKinds.ts`), which
9
+ * checks its value and completes it. The handler receives a typed object the
10
+ * compiler infers from the grammar, written `as const`: its operands, its
11
+ * options and, for a subcommand, which one. Reading an undeclared option is a
12
+ * build error, and the handler never sees the words.
13
+ *
14
+ * A command whose arguments form an expression language (`find`) declares
15
+ * `grammar: 'expression'` instead, keeps its own parser and completion, and
16
+ * receives its words after references expand.
17
+ *
18
+ * Key components:
19
+ * - CommandGrammar: subcommands, operands and options, as a value.
20
+ * - CommandDeclaration: the whole record for a parsed command.
21
+ * - ExpressionDeclaration: the record for an expression-grammar command.
22
+ * - CommandArguments: the typed object a handler receives.
23
+ * - SubcommandArguments: one subcommand's share of it.
24
+ * - command_declare: declares a command, inferring its handler's argument.
25
+ *
26
+ * Typical usage:
27
+ * ```ts
28
+ * const HELLO_GRAMMAR = {
29
+ * operands: [{ name: 'who', kind: 'word', optional: true, summary: 'Whom to greet' }],
30
+ * options: { loud: { short: 'l', long: 'loud', summary: 'Shout' } },
31
+ * } as const satisfies CommandGrammar;
32
+ * export const helloCommand = command_declare({
33
+ * name: 'hello',
34
+ * grammar: HELLO_GRAMMAR,
35
+ * help: { summary: 'Say hello', description: 'Greets someone.' },
36
+ * handler: async (args) => envelope_ok(`hello ${args.operands.who ?? 'world'}${args.options.loud ? '!' : ''}\n`),
37
+ * });
38
+ * ```
39
+ *
40
+ * Dependencies: types only (`@fnndsc/menu` for the envelope, the registry for
41
+ * session pieces).
42
+ *
43
+ * @module
44
+ */
45
+ import type { CommandEnvelope } from '@fnndsc/menu';
46
+ import type { SessionPiece } from './commandRegistry.js';
47
+ /**
48
+ * What an argument is: the kind that checks and completes it, and that
49
+ * kind's parameters.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const sortField: ArgumentGrammar = { kind: 'enum', values: ['name', 'size'] };
54
+ * ```
55
+ */
56
+ export interface ArgumentGrammar {
57
+ /** The kind's name: a core kind (`word`, `enum`, `int`, `number`, `cfsPath`, `hostPath`, `variable`, `job`) or one a backend registers. */
58
+ readonly kind: string;
59
+ /** The words an `enum` takes. */
60
+ readonly values?: readonly string[];
61
+ /** Pathname expansion for a `cfsPath` or `hostPath` operand: a wildcard in it matches paths. */
62
+ readonly glob?: boolean;
63
+ /** The least an `int` or `number` takes. */
64
+ readonly min?: number;
65
+ /** The most an `int` or `number` takes. */
66
+ readonly max?: number;
67
+ }
68
+ /**
69
+ * One operand: a word that is not an option, in its place on the line.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const paths: OperandGrammar = { name: 'paths', kind: 'cfsPath', optional: true, repeat: true, glob: true, summary: 'What to list' };
74
+ * ```
75
+ */
76
+ export interface OperandGrammar extends ArgumentGrammar {
77
+ /** Its name: the property the handler reads, and the word usage shows (`<path>`). */
78
+ readonly name: string;
79
+ /** One line for help and the schema. */
80
+ readonly summary: string;
81
+ /** It may be left out. */
82
+ readonly optional?: boolean;
83
+ /** It takes every word left over: at most one operand of a grammar repeats. */
84
+ readonly repeat?: boolean;
85
+ }
86
+ /** An option's value: an argument with the word usage shows for it (`<field>`). */
87
+ export interface OptionValueGrammar extends ArgumentGrammar {
88
+ /** The word usage shows: `--sort <field>`. */
89
+ readonly name: string;
90
+ }
91
+ /**
92
+ * One option: `-l`, `--long`, or both; a switch, or one that takes a value.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const sort: OptionGrammar = { long: 'sort', summary: 'Sort by', value: { name: 'field', kind: 'enum', values: ['name', 'size'] } };
97
+ * ```
98
+ */
99
+ export interface OptionGrammar {
100
+ /** Its one-character name, without the dash. */
101
+ readonly short?: string;
102
+ /** Its long name, without the dashes. */
103
+ readonly long?: string;
104
+ /** One line for help and the schema. */
105
+ readonly summary: string;
106
+ /** What it takes; absent, it is a switch. */
107
+ readonly value?: OptionValueGrammar;
108
+ /** A value option given more than once keeps every value; otherwise the last one counts. */
109
+ readonly repeat?: boolean;
110
+ }
111
+ /** A grammar's options, by the property the handler reads them as. */
112
+ export type OptionsGrammar = {
113
+ readonly [key: string]: OptionGrammar;
114
+ };
115
+ /**
116
+ * A subcommand: the word after the command, with operands and options of its
117
+ * own. The command's own options hold after it too.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const drop: SubcommandGrammar = { summary: 'Remove one anchor', operands: [{ name: 'name', kind: 'word', summary: 'The anchor' }] };
122
+ * ```
123
+ */
124
+ export interface SubcommandGrammar {
125
+ /** One line for help and the schema. */
126
+ readonly summary: string;
127
+ /** Its operands, in order. */
128
+ readonly operands?: readonly OperandGrammar[];
129
+ /** Its options. */
130
+ readonly options?: OptionsGrammar;
131
+ }
132
+ /**
133
+ * A command's grammar: what its line may hold, as a value. Written `as const`,
134
+ * so the handler's argument is inferred from it.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * const grammar = { operands: [{ name: 'path', kind: 'cfsPath', summary: 'Where' }] } as const satisfies CommandGrammar;
139
+ * ```
140
+ */
141
+ export interface CommandGrammar {
142
+ /** Its operands, in order; with subcommands, the words of the bare command. */
143
+ readonly operands?: readonly OperandGrammar[];
144
+ /** Its options, which hold after any subcommand too. */
145
+ readonly options?: OptionsGrammar;
146
+ /** Its subcommands, by the word that names each. */
147
+ readonly subcommands?: {
148
+ readonly [word: string]: SubcommandGrammar;
149
+ };
150
+ /** What the command does with no subcommand; absent, a command with subcommands needs one. */
151
+ readonly bare?: {
152
+ readonly summary: string;
153
+ };
154
+ }
155
+ /**
156
+ * A command's help prose: what `help` shows beside the usage the grammar
157
+ * generates.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * const prose: CommandProse = { summary: 'List directory contents', description: 'Lists…', category: 'Navigation' };
162
+ * ```
163
+ */
164
+ export interface CommandProse {
165
+ /** One line for listings. */
166
+ readonly summary: string;
167
+ /** What the command does, as one paragraph. */
168
+ readonly description: string;
169
+ /** Lines `help` shows under EXAMPLES; a `#` starts a comment. */
170
+ readonly examples?: readonly string[];
171
+ /** Where `help` lists it: one of the help sections. */
172
+ readonly category?: string;
173
+ /** It mirrors a Unix utility: the group `help POSIX` lists it under, and how it differs, in a line. */
174
+ readonly posix?: {
175
+ readonly group: 'Files' | 'Text' | 'System' | 'Shell';
176
+ readonly differs: string;
177
+ };
178
+ }
179
+ /** A kind's value type: a number for `int` and `number`, the declared words for `enum`, else text. */
180
+ type KindValue<A> = A extends {
181
+ readonly kind: 'int' | 'number';
182
+ } ? number : A extends {
183
+ readonly kind: 'enum';
184
+ readonly values: readonly (infer V extends string)[];
185
+ } ? V : string;
186
+ /** An operand's value: a list when it repeats, possibly absent when optional. */
187
+ type OperandValue<O> = O extends {
188
+ readonly repeat: true;
189
+ } ? KindValue<O>[] : O extends {
190
+ readonly optional: true;
191
+ } ? KindValue<O> | undefined : KindValue<O>;
192
+ /** The operands a grammar declares, by name. */
193
+ type OperandsOf<G> = G extends {
194
+ readonly operands: readonly (infer O)[];
195
+ } ? {
196
+ readonly [P in O as P extends {
197
+ readonly name: infer N extends string;
198
+ } ? N : never]: OperandValue<P>;
199
+ } : Record<never, never>;
200
+ /** The names of a grammar's operands that expand pathnames. */
201
+ type GlobNamesOf<G> = G extends {
202
+ readonly operands: readonly (infer O)[];
203
+ } ? O extends {
204
+ readonly glob: true;
205
+ readonly name: infer N extends string;
206
+ } ? N : never : never;
207
+ /** Whether each globbing operand's words came from pathname expansion. */
208
+ type ExpandedOf<G> = {
209
+ readonly [N in GlobNamesOf<G>]: boolean;
210
+ };
211
+ /** An option's value: a switch is a boolean; a value option its value, or every value when it repeats. */
212
+ type OptionValue<O> = O extends {
213
+ readonly value: infer V;
214
+ } ? O extends {
215
+ readonly repeat: true;
216
+ } ? KindValue<V>[] : KindValue<V> | undefined : boolean;
217
+ /** The options a grammar declares, by property. */
218
+ type OptionsOf<G> = G extends {
219
+ readonly options: infer M;
220
+ } ? {
221
+ readonly [K in keyof M]: OptionValue<M[K]>;
222
+ } : Record<never, never>;
223
+ /** One parsed line: what a handler reads, for a grammar and the command's own options. */
224
+ interface ParsedLine<S, G> {
225
+ /** The operands, by name. */
226
+ readonly operands: OperandsOf<S>;
227
+ /** The options, by property: the subcommand's and the command's. */
228
+ readonly options: OptionsOf<S> & OptionsOf<G>;
229
+ /** For each operand that globs, whether its words came from pathname expansion. */
230
+ readonly expanded: ExpandedOf<S>;
231
+ }
232
+ /** The bare command's line, when the grammar allows one. */
233
+ type BareLine<G> = G extends {
234
+ readonly subcommands: object;
235
+ } ? G extends {
236
+ readonly bare: object;
237
+ } ? {
238
+ readonly subcommand: null;
239
+ } & ParsedLine<G, G> : never : {
240
+ readonly subcommand: null;
241
+ } & ParsedLine<G, G>;
242
+ /** Each subcommand's line. */
243
+ type SubcommandLine<G> = G extends {
244
+ readonly subcommands: infer S;
245
+ } ? {
246
+ [K in keyof S & string]: {
247
+ readonly subcommand: K;
248
+ } & ParsedLine<S[K], G>;
249
+ }[keyof S & string] : never;
250
+ /**
251
+ * The typed object a handler receives: which subcommand (null for none), its
252
+ * operands, its options, and which operands came from pathname expansion.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * type LsArguments = CommandArguments<typeof LS_GRAMMAR>;
257
+ * const paths: string[] = (args as LsArguments).operands.paths;
258
+ * ```
259
+ */
260
+ export type CommandArguments<G extends CommandGrammar> = BareLine<G> | SubcommandLine<G>;
261
+ /**
262
+ * One subcommand's share of a handler's argument, for a function that runs it.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * function drop_run(args: SubcommandArguments<typeof BERTH_GRAMMAR, 'drop'>): CommandEnvelope { … }
267
+ * ```
268
+ */
269
+ export type SubcommandArguments<G extends CommandGrammar, K extends string> = Extract<CommandArguments<G>, {
270
+ readonly subcommand: K;
271
+ }>;
272
+ /**
273
+ * A command parsed against its grammar: the one record that is its help,
274
+ * its completion, its schema and its dispatch.
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * const declaration: CommandDeclaration<typeof HELLO_GRAMMAR> = helloCommand;
279
+ * ```
280
+ */
281
+ export interface CommandDeclaration<G extends CommandGrammar = CommandGrammar> {
282
+ /** The word that runs it. */
283
+ readonly name: string;
284
+ /** What its line may hold. */
285
+ readonly grammar: G;
286
+ /** What `help` says of it. */
287
+ readonly help: CommandProse;
288
+ /** The pieces of the session it changes while it runs; absent, it holds the whole session. */
289
+ readonly holds?: readonly SessionPiece[];
290
+ /**
291
+ * Runs it, given the line parsed and checked. A method, so a record of one
292
+ * grammar stands where any declaration is read.
293
+ *
294
+ * @param args - The typed line.
295
+ * @returns Its outcome.
296
+ */
297
+ handler(args: CommandArguments<G>): Promise<CommandEnvelope>;
298
+ }
299
+ /**
300
+ * A command whose arguments are an expression language of its own (`find`):
301
+ * it parses and completes them itself, under a ratchet.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * const findCommand: ExpressionDeclaration = { name: 'find', grammar: 'expression', help, handler: builtin_find };
306
+ * ```
307
+ */
308
+ export interface ExpressionDeclaration {
309
+ /** The word that runs it. */
310
+ readonly name: string;
311
+ /** Says it parses its own words. */
312
+ readonly grammar: 'expression';
313
+ /** What `help` says of it; its usage is its own. */
314
+ readonly help: CommandProse & {
315
+ readonly usage: string;
316
+ };
317
+ /** The pieces of the session it changes while it runs. */
318
+ readonly holds?: readonly SessionPiece[];
319
+ /**
320
+ * Runs it over its words, references expanded and nothing globbed.
321
+ *
322
+ * @param args - The words after the command.
323
+ * @returns Its outcome.
324
+ */
325
+ handler(args: string[]): Promise<CommandEnvelope>;
326
+ /**
327
+ * Completes one word of its line.
328
+ *
329
+ * @param words - The words before the one completed.
330
+ * @param word - The word typed so far.
331
+ * @returns The candidates.
332
+ */
333
+ complete?(words: readonly string[], word: string): Promise<string[]>;
334
+ }
335
+ /** Any declaration the registry holds. */
336
+ export type AnyCommandDeclaration = CommandDeclaration | ExpressionDeclaration;
337
+ /**
338
+ * Declares a command. Returns its argument unchanged; what it adds is the
339
+ * inference: the handler's argument is typed from the grammar beside it.
340
+ *
341
+ * @param declaration - The record.
342
+ * @returns The same record.
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * export const helloCommand = command_declare({ name: 'hello', grammar: HELLO_GRAMMAR, help, handler: async (args) => … });
347
+ * ```
348
+ */
349
+ export declare function command_declare<const G extends CommandGrammar>(declaration: CommandDeclaration<G>): CommandDeclaration<G>;
350
+ /**
351
+ * Whether a declaration parses its line against a grammar (rather than an
352
+ * expression language of its own).
353
+ *
354
+ * @param declaration - The record.
355
+ * @returns True for a grammar.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * if (declaration_isParsed(declaration)) declaration.grammar.operands;
360
+ * ```
361
+ */
362
+ export declare function declaration_isParsed(declaration: AnyCommandDeclaration): declaration is CommandDeclaration;
363
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Declares a command. Returns its argument unchanged; what it adds is the
3
+ * inference: the handler's argument is typed from the grammar beside it.
4
+ *
5
+ * @param declaration - The record.
6
+ * @returns The same record.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * export const helloCommand = command_declare({ name: 'hello', grammar: HELLO_GRAMMAR, help, handler: async (args) => … });
11
+ * ```
12
+ */
13
+ export function command_declare(declaration) {
14
+ return declaration;
15
+ }
16
+ /**
17
+ * Whether a declaration parses its line against a grammar (rather than an
18
+ * expression language of its own).
19
+ *
20
+ * @param declaration - The record.
21
+ * @returns True for a grammar.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * if (declaration_isParsed(declaration)) declaration.grammar.operands;
26
+ * ```
27
+ */
28
+ export function declaration_isParsed(declaration) {
29
+ return declaration.grammar !== 'expression';
30
+ }
31
+ //# sourceMappingURL=commandDeclaration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandDeclaration.js","sourceRoot":"","sources":["../../src/core/commandDeclaration.ts"],"names":[],"mappings":"AAoUA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAiC,WAAkC;IAChG,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAkC;IACrE,OAAO,WAAW,CAAC,OAAO,KAAK,YAAY,CAAC;AAC9C,CAAC"}