@fnndsc/chili 3.2.5

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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/chefs/chefs.d.ts +15 -0
  4. package/dist/chefs/chefs.js +195 -0
  5. package/dist/commands/compute/fields.d.ts +6 -0
  6. package/dist/commands/compute/fields.js +13 -0
  7. package/dist/commands/compute/list.d.ts +19 -0
  8. package/dist/commands/compute/list.js +20 -0
  9. package/dist/commands/connect/login.d.ts +16 -0
  10. package/dist/commands/connect/login.js +18 -0
  11. package/dist/commands/connect/logout.d.ts +6 -0
  12. package/dist/commands/connect/logout.js +16 -0
  13. package/dist/commands/feed/comments.d.ts +41 -0
  14. package/dist/commands/feed/comments.js +45 -0
  15. package/dist/commands/feed/create.d.ts +13 -0
  16. package/dist/commands/feed/create.js +40 -0
  17. package/dist/commands/feed/note.d.ts +21 -0
  18. package/dist/commands/feed/note.js +24 -0
  19. package/dist/commands/feeds/delete.d.ts +14 -0
  20. package/dist/commands/feeds/delete.js +34 -0
  21. package/dist/commands/feeds/fields.d.ts +6 -0
  22. package/dist/commands/feeds/fields.js +17 -0
  23. package/dist/commands/feeds/list.d.ts +17 -0
  24. package/dist/commands/feeds/list.js +38 -0
  25. package/dist/commands/feeds/share.d.ts +17 -0
  26. package/dist/commands/feeds/share.js +19 -0
  27. package/dist/commands/file/view.d.ts +8 -0
  28. package/dist/commands/file/view.js +25 -0
  29. package/dist/commands/files/delete.d.ts +16 -0
  30. package/dist/commands/files/delete.js +36 -0
  31. package/dist/commands/files/fields.d.ts +7 -0
  32. package/dist/commands/files/fields.js +18 -0
  33. package/dist/commands/files/list.d.ts +11 -0
  34. package/dist/commands/files/list.js +23 -0
  35. package/dist/commands/fs/cat.d.ts +15 -0
  36. package/dist/commands/fs/cat.js +26 -0
  37. package/dist/commands/fs/cp.d.ts +17 -0
  38. package/dist/commands/fs/cp.js +26 -0
  39. package/dist/commands/fs/create.d.ts +15 -0
  40. package/dist/commands/fs/create.js +83 -0
  41. package/dist/commands/fs/download.d.ts +25 -0
  42. package/dist/commands/fs/download.js +235 -0
  43. package/dist/commands/fs/edit.d.ts +16 -0
  44. package/dist/commands/fs/edit.js +25 -0
  45. package/dist/commands/fs/ls.d.ts +18 -0
  46. package/dist/commands/fs/ls.js +142 -0
  47. package/dist/commands/fs/mkdir.d.ts +8 -0
  48. package/dist/commands/fs/mkdir.js +21 -0
  49. package/dist/commands/fs/mv.d.ts +13 -0
  50. package/dist/commands/fs/mv.js +73 -0
  51. package/dist/commands/fs/rm.d.ts +24 -0
  52. package/dist/commands/fs/rm.js +108 -0
  53. package/dist/commands/fs/touch.d.ts +16 -0
  54. package/dist/commands/fs/touch.js +62 -0
  55. package/dist/commands/fs/upload.d.ts +55 -0
  56. package/dist/commands/fs/upload.js +221 -0
  57. package/dist/commands/groups/fields.d.ts +6 -0
  58. package/dist/commands/groups/fields.js +13 -0
  59. package/dist/commands/groups/list.d.ts +16 -0
  60. package/dist/commands/groups/list.js +26 -0
  61. package/dist/commands/man/doc.d.ts +16 -0
  62. package/dist/commands/man/doc.js +32 -0
  63. package/dist/commands/man/topics.d.ts +6 -0
  64. package/dist/commands/man/topics.js +27 -0
  65. package/dist/commands/pipeline/fields.d.ts +6 -0
  66. package/dist/commands/pipeline/fields.js +13 -0
  67. package/dist/commands/pipeline/run.d.ts +18 -0
  68. package/dist/commands/pipeline/run.js +33 -0
  69. package/dist/commands/plugin/readme.d.ts +14 -0
  70. package/dist/commands/plugin/readme.js +49 -0
  71. package/dist/commands/plugin/run.d.ts +10 -0
  72. package/dist/commands/plugin/run.js +29 -0
  73. package/dist/commands/plugin/search.d.ts +7 -0
  74. package/dist/commands/plugin/search.js +18 -0
  75. package/dist/commands/plugininstances/fields.d.ts +6 -0
  76. package/dist/commands/plugininstances/fields.js +13 -0
  77. package/dist/commands/plugininstances/list.d.ts +16 -0
  78. package/dist/commands/plugininstances/list.js +26 -0
  79. package/dist/commands/pluginmetas/fields.d.ts +6 -0
  80. package/dist/commands/pluginmetas/fields.js +13 -0
  81. package/dist/commands/pluginmetas/list.d.ts +16 -0
  82. package/dist/commands/pluginmetas/list.js +26 -0
  83. package/dist/commands/plugins/add.d.ts +34 -0
  84. package/dist/commands/plugins/add.js +374 -0
  85. package/dist/commands/plugins/delete.d.ts +14 -0
  86. package/dist/commands/plugins/delete.js +34 -0
  87. package/dist/commands/plugins/fields.d.ts +6 -0
  88. package/dist/commands/plugins/fields.js +17 -0
  89. package/dist/commands/plugins/list.d.ts +17 -0
  90. package/dist/commands/plugins/list.js +40 -0
  91. package/dist/commands/plugins/overview.d.ts +6 -0
  92. package/dist/commands/plugins/overview.js +17 -0
  93. package/dist/commands/store/list.d.ts +23 -0
  94. package/dist/commands/store/list.js +48 -0
  95. package/dist/commands/tags/fields.d.ts +6 -0
  96. package/dist/commands/tags/fields.js +13 -0
  97. package/dist/commands/tags/list.d.ts +16 -0
  98. package/dist/commands/tags/list.js +26 -0
  99. package/dist/commands/workflows/fields.d.ts +6 -0
  100. package/dist/commands/workflows/fields.js +13 -0
  101. package/dist/commands/workflows/list.d.ts +16 -0
  102. package/dist/commands/workflows/list.js +26 -0
  103. package/dist/config/colorConfig.d.ts +56 -0
  104. package/dist/config/colorConfig.js +161 -0
  105. package/dist/connect/connectHandler.d.ts +15 -0
  106. package/dist/connect/connectHandler.js +48 -0
  107. package/dist/context/contextCommand.d.ts +13 -0
  108. package/dist/context/contextCommand.js +193 -0
  109. package/dist/controllers/baseController.d.ts +43 -0
  110. package/dist/controllers/baseController.js +67 -0
  111. package/dist/controllers/feedController.d.ts +35 -0
  112. package/dist/controllers/feedController.js +53 -0
  113. package/dist/controllers/fileController.d.ts +56 -0
  114. package/dist/controllers/fileController.js +93 -0
  115. package/dist/controllers/pluginContextController.d.ts +24 -0
  116. package/dist/controllers/pluginContextController.js +49 -0
  117. package/dist/controllers/pluginController.d.ts +70 -0
  118. package/dist/controllers/pluginController.js +98 -0
  119. package/dist/controllers/pluginMetaController.d.ts +41 -0
  120. package/dist/controllers/pluginMetaController.js +52 -0
  121. package/dist/feeds/feedHandler.d.ts +64 -0
  122. package/dist/feeds/feedHandler.js +203 -0
  123. package/dist/filesystem/fileGroupHandler.d.ts +92 -0
  124. package/dist/filesystem/fileGroupHandler.js +318 -0
  125. package/dist/filesystem/filesystemHandler.d.ts +12 -0
  126. package/dist/filesystem/filesystemHandler.js +135 -0
  127. package/dist/filesystem/inodeCommand.d.ts +12 -0
  128. package/dist/filesystem/inodeCommand.js +37 -0
  129. package/dist/handlers/baseGroupHandler.d.ts +103 -0
  130. package/dist/handlers/baseGroupHandler.js +342 -0
  131. package/dist/index.d.ts +7 -0
  132. package/dist/index.js +239 -0
  133. package/dist/lfs/lfs.d.ts +25 -0
  134. package/dist/lfs/lfs.js +74 -0
  135. package/dist/man/man.d.ts +15 -0
  136. package/dist/man/man.js +31 -0
  137. package/dist/man/renderer.d.ts +26 -0
  138. package/dist/man/renderer.js +187 -0
  139. package/dist/models/feed.d.ts +18 -0
  140. package/dist/models/feed.js +8 -0
  141. package/dist/models/listing.d.ts +33 -0
  142. package/dist/models/listing.js +9 -0
  143. package/dist/models/plugin.d.ts +27 -0
  144. package/dist/models/plugin.js +1 -0
  145. package/dist/models/resource.d.ts +39 -0
  146. package/dist/models/resource.js +9 -0
  147. package/dist/pacs/pacsQueryHandler.d.ts +55 -0
  148. package/dist/pacs/pacsQueryHandler.js +196 -0
  149. package/dist/pacs/pacsQueryPayload.d.ts +19 -0
  150. package/dist/pacs/pacsQueryPayload.js +44 -0
  151. package/dist/pacs/pacsResultRender.d.ts +20 -0
  152. package/dist/pacs/pacsResultRender.js +141 -0
  153. package/dist/pacs/pacsRetrieveHandler.d.ts +87 -0
  154. package/dist/pacs/pacsRetrieveHandler.js +272 -0
  155. package/dist/pacs/pacsServerHandler.d.ts +15 -0
  156. package/dist/pacs/pacsServerHandler.js +32 -0
  157. package/dist/path/pathCommand.d.ts +64 -0
  158. package/dist/path/pathCommand.js +805 -0
  159. package/dist/path/pathMapper.d.ts +189 -0
  160. package/dist/path/pathMapper.js +382 -0
  161. package/dist/plugins/pluginGroupHandler.d.ts +42 -0
  162. package/dist/plugins/pluginGroupHandler.js +90 -0
  163. package/dist/plugins/pluginHandler.d.ts +87 -0
  164. package/dist/plugins/pluginHandler.js +303 -0
  165. package/dist/plugins/pluginMetaHandler.d.ts +21 -0
  166. package/dist/plugins/pluginMetaHandler.js +27 -0
  167. package/dist/screen/screen.d.ts +214 -0
  168. package/dist/screen/screen.js +478 -0
  169. package/dist/utils/admin_prompt.d.ts +42 -0
  170. package/dist/utils/admin_prompt.js +105 -0
  171. package/dist/utils/cli.d.ts +67 -0
  172. package/dist/utils/cli.js +112 -0
  173. package/dist/utils/docker.d.ts +74 -0
  174. package/dist/utils/docker.js +155 -0
  175. package/dist/utils/input_format.d.ts +77 -0
  176. package/dist/utils/input_format.js +135 -0
  177. package/dist/utils/sort.d.ts +29 -0
  178. package/dist/utils/sort.js +63 -0
  179. package/dist/utils/ui.d.ts +7 -0
  180. package/dist/utils/ui.js +26 -0
  181. package/dist/utils.d.ts +15 -0
  182. package/dist/utils.js +14 -0
  183. package/dist/views/compute.d.ts +17 -0
  184. package/dist/views/compute.js +40 -0
  185. package/dist/views/connect.d.ts +13 -0
  186. package/dist/views/connect.js +35 -0
  187. package/dist/views/feed.d.ts +44 -0
  188. package/dist/views/feed.js +110 -0
  189. package/dist/views/file.d.ts +25 -0
  190. package/dist/views/file.js +71 -0
  191. package/dist/views/fs.d.ts +51 -0
  192. package/dist/views/fs.js +105 -0
  193. package/dist/views/ls.d.ts +37 -0
  194. package/dist/views/ls.js +190 -0
  195. package/dist/views/plugin.d.ts +28 -0
  196. package/dist/views/plugin.js +71 -0
  197. package/dist/views/pluginParameters.d.ts +17 -0
  198. package/dist/views/pluginParameters.js +79 -0
  199. package/docs/00_intro.adoc +25 -0
  200. package/docs/01_setup.adoc +63 -0
  201. package/docs/02_context.adoc +128 -0
  202. package/docs/03_searchable.adoc +134 -0
  203. package/docs/10_everyday_examples.adoc +9 -0
  204. package/docs/11_upload_data.adoc +146 -0
  205. package/docs/12_create_a_feed.adoc +9 -0
  206. package/docs/13_run_a_plugin.adoc +271 -0
  207. package/docs/20_plugins.adoc +16 -0
  208. package/docs/21_pluginReadme.adoc +123 -0
  209. package/docs/30_filesystem.adoc +93 -0
  210. package/docs/31_fileops.adoc +51 -0
  211. package/docs/32_file_content.adoc +471 -0
  212. package/docs/_architecture.adoc +48 -0
  213. package/docs/_deepContext.adoc +84 -0
  214. package/docs/_roadmap.adoc +42 -0
  215. package/docs/_searchable.adoc +187 -0
  216. package/docs/_state.adoc +68 -0
  217. package/docs/pacs.adoc +101 -0
  218. package/docs/pathMapper.md +246 -0
  219. package/package.json +92 -0
@@ -0,0 +1,471 @@
1
+ = File Content Retrieval in ChILI
2
+ :toc:
3
+ :toc-placement: preamble
4
+ :sectnums:
5
+
6
+ == Overview
7
+
8
+ This document describes how ChILI commands and controllers consume the file content retrieval functionality provided by the `salsa` business logic layer. It focuses on the integration patterns used in ChILI's command layer.
9
+
10
+ For comprehensive architecture and implementation details, see: `salsa/docs/file-content-retrieval.adoc`
11
+
12
+ == Integration Pattern
13
+
14
+ ChILI commands follow this pattern:
15
+
16
+ 1. **Parse arguments** - Extract file path from user input
17
+ 2. **Resolve path** - Convert relative → absolute using `path_resolveChrisFs()`
18
+ 3. **Call salsa** - Invoke `fileContent_get(filePath)`
19
+ 4. **Handle result** - Check `Result<T>`, pop errors from `errorStack`
20
+ 5. **Display output** - Render content or error message
21
+
22
+ == Commands Using File Content
23
+
24
+ === 1. `cat` Command
25
+
26
+ **File:** `src/commands/fs/cat.ts`
27
+
28
+ **Purpose:** Display file content (like Unix `cat`)
29
+
30
+ [source,typescript]
31
+ ----
32
+ import { fileContent_get } from "@fnndsc/salsa";
33
+ import { path_resolveChrisFs } from "../../utils/cli.js";
34
+ import { Result } from "@fnndsc/cumin";
35
+
36
+ /**
37
+ * Retrieves the content of a file.
38
+ *
39
+ * @param filePath - The path to the file.
40
+ * @returns Promise resolving to content string or error.
41
+ */
42
+ export async function files_cat(filePath: string): Promise<Result<string>> {
43
+ const resolved: string = await path_resolveChrisFs(filePath, {});
44
+ return await fileContent_get(resolved);
45
+ }
46
+ ----
47
+
48
+ **Key Points:**
49
+
50
+ * **RPN naming:** Function named `files_cat` (not `catFile` or `getCatContent`)
51
+ * **Path resolution:** Always resolve relative paths before calling salsa
52
+ * **Pass-through:** Returns `Result<T>` directly to caller for error handling
53
+ * **No console output:** Command logic is pure, caller decides how to display
54
+
55
+ **Usage in CLI:**
56
+
57
+ [source,bash]
58
+ ----
59
+ chili chefs cat /home/user/file.txt
60
+ chili chefs cat ./relative/path.txt
61
+ chili chefs cat /PIPELINES/user/spec.yml
62
+ ----
63
+
64
+ ---
65
+
66
+ === 2. `view` Command
67
+
68
+ **File:** `src/commands/file/view.ts`
69
+
70
+ **Purpose:** View content of a specific file (supports ID or path)
71
+
72
+ [source,typescript]
73
+ ----
74
+ import { fileContent_get } from "@fnndsc/salsa";
75
+ import { Result } from "@fnndsc/cumin";
76
+
77
+ /**
78
+ * Fetches the content of a ChRIS file as a UTF-8 string.
79
+ *
80
+ * @param fileIdentifier - The path of the file to view.
81
+ * @returns A Promise resolving to the file content as a string, or `null` if empty/not found.
82
+ * @throws {Error} If the operation fails and an error is pushed to the stack.
83
+ */
84
+ export async function files_viewContent(fileIdentifier: string): Promise<string | null> {
85
+ const result: Result<string> = await fileContent_get(fileIdentifier);
86
+
87
+ if (!result.ok) {
88
+ // Errors are pushed to the stack within salsa
89
+ throw new Error(`Failed to view content for: ${fileIdentifier}`);
90
+ }
91
+
92
+ return result.value;
93
+ }
94
+ ----
95
+
96
+ **Key Points:**
97
+
98
+ * **Alternative error handling:** Throws exception instead of returning `Result<T>`
99
+ * **Why?** This function is called by controller layer which expects exceptions
100
+ * **Pattern:** `Result<T>` → exception conversion for legacy compatibility
101
+
102
+ **Usage in CLI:**
103
+
104
+ [source,bash]
105
+ ----
106
+ chili file view /home/user/data.json
107
+ ----
108
+
109
+ ---
110
+
111
+ === 3. FileController
112
+
113
+ **File:** `src/controllers/fileController.ts`
114
+
115
+ **Purpose:** Controller-level abstraction for file operations
116
+
117
+ [source,typescript]
118
+ ----
119
+ import {
120
+ fileContent_get,
121
+ FileShareOptions
122
+ } from "@fnndsc/salsa";
123
+ import { Result, errorStack } from "@fnndsc/cumin";
124
+ import chalk from 'chalk';
125
+
126
+ export class FileController extends BaseController {
127
+ /**
128
+ * Views content of a file using salsa logic.
129
+ *
130
+ * @param filePath - The ChRIS path to the file.
131
+ * @returns A Promise resolving to void (output is logged).
132
+ */
133
+ async file_view(filePath: string): Promise<void> {
134
+ const result: Result<string> = await fileContent_get(filePath);
135
+ if (!result.ok) {
136
+ const error = errorStack.stack_pop();
137
+ console.error(chalk.red(`Error viewing file: ${error?.message || 'Unknown error'}`));
138
+ return;
139
+ }
140
+ console.log(result.value);
141
+ }
142
+ }
143
+ ----
144
+
145
+ **Key Points:**
146
+
147
+ * **Controller responsibility:** Handle display/formatting
148
+ * **Error rendering:** Uses `chalk` for colored error messages
149
+ * **Result handling:** Explicit `!result.ok` check, error pop, display
150
+ * **Separation:** Business logic in salsa, presentation in controller
151
+
152
+ ---
153
+
154
+ == Import Patterns
155
+
156
+ === Standard Import
157
+
158
+ [source,typescript]
159
+ ----
160
+ // Named import (preferred)
161
+ import { fileContent_get } from "@fnndsc/salsa";
162
+
163
+ // Usage
164
+ const result = await fileContent_get(path);
165
+ ----
166
+
167
+ === Avoiding Conflicts
168
+
169
+ If you need to import multiple `*_get` functions:
170
+
171
+ [source,typescript]
172
+ ----
173
+ import {
174
+ fileContent_get,
175
+ fileFields_get,
176
+ feedContent_get
177
+ } from "@fnndsc/salsa";
178
+ ----
179
+
180
+ Names are distinct due to RPN convention (subject comes first).
181
+
182
+ ---
183
+
184
+ == Error Handling Patterns
185
+
186
+ === Pattern 1: Return Result<T>
187
+
188
+ **When:** Command logic that doesn't display output
189
+
190
+ [source,typescript]
191
+ ----
192
+ export async function files_cat(filePath: string): Promise<Result<string>> {
193
+ const resolved: string = await path_resolveChrisFs(filePath, {});
194
+ return await fileContent_get(resolved); // Pass-through
195
+ }
196
+ ----
197
+
198
+ **Caller:**
199
+ [source,typescript]
200
+ ----
201
+ const result = await files_cat(path);
202
+ if (!result.ok) {
203
+ const error = errorStack.stack_pop();
204
+ console.error(chalk.red(error?.message));
205
+ return;
206
+ }
207
+ console.log(result.value);
208
+ ----
209
+
210
+ ---
211
+
212
+ === Pattern 2: Throw Exception
213
+
214
+ **When:** Integration with legacy code expecting exceptions
215
+
216
+ [source,typescript]
217
+ ----
218
+ export async function files_viewContent(fileId: string): Promise<string | null> {
219
+ const result: Result<string> = await fileContent_get(fileId);
220
+
221
+ if (!result.ok) {
222
+ throw new Error(`Failed to view content for: ${fileId}`);
223
+ }
224
+
225
+ return result.value;
226
+ }
227
+ ----
228
+
229
+ **Caller:**
230
+ [source,typescript]
231
+ ----
232
+ try {
233
+ const content = await files_viewContent(path);
234
+ console.log(content);
235
+ } catch (error) {
236
+ console.error(`Error: ${error.message}`);
237
+ }
238
+ ----
239
+
240
+ ---
241
+
242
+ === Pattern 3: Display Directly
243
+
244
+ **When:** Controller methods that handle both logic and display
245
+
246
+ [source,typescript]
247
+ ----
248
+ async file_view(filePath: string): Promise<void> {
249
+ const result: Result<string> = await fileContent_get(filePath);
250
+ if (!result.ok) {
251
+ const error = errorStack.stack_pop();
252
+ console.error(chalk.red(`Error: ${error?.message}`));
253
+ return;
254
+ }
255
+ console.log(result.value);
256
+ }
257
+ ----
258
+
259
+ == Path Resolution
260
+
261
+ Always resolve paths before calling salsa functions:
262
+
263
+ [source,typescript]
264
+ ----
265
+ import { path_resolveChrisFs } from "../../utils/cli.js";
266
+
267
+ // User input (may be relative)
268
+ const userPath = "./data/file.txt";
269
+
270
+ // Resolve to absolute
271
+ const resolved: string = await path_resolveChrisFs(userPath, {});
272
+ // Result: "/home/chris/data/file.txt"
273
+
274
+ // Now safe to call salsa
275
+ const result = await fileContent_get(resolved);
276
+ ----
277
+
278
+ **Why?**
279
+
280
+ * User may provide relative paths
281
+ * Salsa expects absolute paths
282
+ * Path resolution respects current working directory
283
+
284
+ == Adding New Commands
285
+
286
+ To add a new command that uses file content:
287
+
288
+ === Step 1: Create Command File
289
+
290
+ [source,typescript]
291
+ ----
292
+ // src/commands/mycommand/process.ts
293
+ import { fileContent_get } from "@fnndsc/salsa";
294
+ import { path_resolveChrisFs } from "../../utils/cli.js";
295
+ import { Result } from "@fnndsc/cumin";
296
+
297
+ /**
298
+ * Process a file's content.
299
+ *
300
+ * @param filePath - Path to the file.
301
+ * @returns Result with processed content.
302
+ */
303
+ export async function fileContent_process(filePath: string): Promise<Result<string>> {
304
+ const resolved: string = await path_resolveChrisFs(filePath, {});
305
+ const result: Result<string> = await fileContent_get(resolved);
306
+
307
+ if (!result.ok) {
308
+ return result; // Pass through error
309
+ }
310
+
311
+ // Process content
312
+ const processed: string = result.value.toUpperCase(); // Example
313
+ return Ok(processed);
314
+ }
315
+ ----
316
+
317
+ === Step 2: Add CLI Handler
318
+
319
+ [source,typescript]
320
+ ----
321
+ // src/index.ts or appropriate CLI router
322
+ import { fileContent_process } from './commands/mycommand/process.js';
323
+
324
+ // In command handler
325
+ case 'process':
326
+ const result = await fileContent_process(args.file);
327
+ if (!result.ok) {
328
+ const error = errorStack.stack_pop();
329
+ console.error(chalk.red(error?.message));
330
+ } else {
331
+ console.log(result.value);
332
+ }
333
+ break;
334
+ ----
335
+
336
+ === Step 3: Add Tests
337
+
338
+ [source,typescript]
339
+ ----
340
+ // tests/mycommand.test.ts
341
+ import { fileContent_process } from '../src/commands/mycommand/process';
342
+
343
+ describe('fileContent_process', () => {
344
+ it('should process file content', async () => {
345
+ // Mock fileContent_get
346
+ const result = await fileContent_process('/test/file.txt');
347
+ expect(result.ok).toBe(true);
348
+ });
349
+ });
350
+ ----
351
+
352
+ == Testing
353
+
354
+ === Mocking File Content
355
+
356
+ [source,typescript]
357
+ ----
358
+ import { fileContent_get } from "@fnndsc/salsa";
359
+
360
+ // Mock in test setup
361
+ jest.mock('@fnndsc/salsa', () => ({
362
+ fileContent_get: jest.fn()
363
+ }));
364
+
365
+ // In test
366
+ const mockFileContent = fileContent_get as jest.MockedFunction<typeof fileContent_get>;
367
+ mockFileContent.mockResolvedValue({
368
+ ok: true,
369
+ value: 'mock file content'
370
+ });
371
+
372
+ // Test your command
373
+ const result = await files_cat('/test/file.txt');
374
+ expect(result.ok).toBe(true);
375
+ expect(result.value).toBe('mock file content');
376
+ ----
377
+
378
+ == Debugging
379
+
380
+ === Enable Verbose Logging
381
+
382
+ Temporarily add logging in commands:
383
+
384
+ [source,typescript]
385
+ ----
386
+ export async function files_cat(filePath: string): Promise<Result<string>> {
387
+ console.error(`[DEBUG] files_cat called with: ${filePath}`);
388
+
389
+ const resolved: string = await path_resolveChrisFs(filePath, {});
390
+ console.error(`[DEBUG] Resolved to: ${resolved}`);
391
+
392
+ const result = await fileContent_get(resolved);
393
+ console.error(`[DEBUG] Result ok: ${result.ok}`);
394
+
395
+ return result;
396
+ }
397
+ ----
398
+
399
+ **Remember to remove debug statements before committing.**
400
+
401
+ === Check Error Stack
402
+
403
+ If errors are unclear:
404
+
405
+ [source,typescript]
406
+ ----
407
+ const result = await fileContent_get(path);
408
+ if (!result.ok) {
409
+ // Pop ALL errors to see full stack
410
+ while (true) {
411
+ const error = errorStack.stack_pop();
412
+ if (!error) break;
413
+ console.error(`[${error.level}] ${error.message}`);
414
+ }
415
+ }
416
+ ----
417
+
418
+ == Common Issues
419
+
420
+ [cols="2,3,3"]
421
+ |===
422
+ | Issue | Cause | Solution
423
+
424
+ | **Relative path fails**
425
+ | Path not resolved
426
+ | Use `path_resolveChrisFs()` before calling salsa
427
+
428
+ | **Error not displayed**
429
+ | Forgot to pop errorStack
430
+ | Always `errorStack.stack_pop()` after `Err()`
431
+
432
+ | **Pipeline files fail**
433
+ | Wrong path format
434
+ | Ensure path starts with `/PIPELINES/`
435
+
436
+ | **File not found**
437
+ | Typo or permissions
438
+ | Test with `chili chefs ls` first
439
+ |===
440
+
441
+ == Performance Tips
442
+
443
+ 1. **Don't fetch unnecessarily**
444
+ - Check if file exists with `ls` before `cat`
445
+ - Cache results if displaying multiple times
446
+
447
+ 2. **Large files**
448
+ - Consider streaming for files > 10MB
449
+ - Warn user before fetching huge files
450
+
451
+ 3. **Batch operations**
452
+ - Use `Promise.all()` for multiple files
453
+ - But respect API rate limits
454
+
455
+ == Related Documentation
456
+
457
+ * **Comprehensive Architecture:** `salsa/docs/file-content-retrieval.adoc`
458
+ * **VFS Routing:** `chell/docs/vfs.adoc`
459
+ * **Error Handling:** `cumin/docs/error_handling.adoc`
460
+ * **RPN Naming:** `TYPESCRIPT-STYLE-GUIDE.md`
461
+
462
+ == Change Log
463
+
464
+ [cols="1,2,4"]
465
+ |===
466
+ | Version | Date | Changes
467
+
468
+ | 1.0.0
469
+ | 2025-12-09
470
+ | Initial chili integration documentation
471
+ |===
@@ -0,0 +1,48 @@
1
+ = ChUI Architecture: The "Command as Library" Model
2
+
3
+ == Introduction
4
+
5
+ The ChUI (ChRIS User Interface) codebase, encompassing `chili`, `salsa`, and `cumin`, follows a strict layered "Sandwich" architecture. This design decouples business logic from presentation, enabling the core functionality to be consumed by multiple frontends—specifically the `chili` CLI and the `chell` interactive shell.
6
+
7
+ == Core Principles
8
+
9
+ . **Single Source of Truth:** `chili` commands serve as the definitive implementation for app-level logic.
10
+ . **Separation of Concerns:** Logic (`salsa`), Control (`chili commands`), and Presentation (`chili views`) are distinct.
11
+ . **Pervasive Typing:** Data exchange between layers is governed by strict TypeScript interfaces (Models).
12
+
13
+ == The Layered "Sandwich" Model
14
+
15
+ The architecture is composed of three primary layers:
16
+
17
+ [cols="1,3"]
18
+ |===
19
+ | Layer | Responsibility
20
+
21
+ | *`chili`* (Controller & View)
22
+ | The top layer is split into sub-layers:
23
+ * **Commands (`commands/`)**: Headless controllers that call `salsa` and return strictly typed **Models**. They are UI-agnostic and consumable by `chell`.
24
+ * **Views (`views/`)**: Pure functions that accept Models and return formatted strings/tables.
25
+ * **Handlers (`handlers/` & `chefs/`)**: The CLI entry points. They parse arguments, invoke Commands, and pass the results to Views.
26
+ * **Models (`models/`)**: TypeScript interfaces (`src/models/`) defining the data structures exchanged between layers.
27
+
28
+ | *`salsa`* (Business Logic)
29
+ | The core, reusable library of "Intents". It contains the "how" for every action (e.g., `feed_create`, `files_uploadPath`). This layer wraps `cumin` and returns raw data or success status.
30
+
31
+ | *`cumin`* (Infrastructure)
32
+ | The lowest-level library that manages state, connectivity, and raw I/O. It provides `IStorageProvider` abstractions for filesystem access (Node vs Browser) and `ChrisIO` for recursive operations.
33
+ |===
34
+
35
+ == Data Flow Example: `ls`
36
+
37
+ When a user runs `ls` (in CLI or Shell):
38
+
39
+ 1. **CLI/Shell**: Calls `chili/commands/fs/ls.ts`.
40
+ 2. **Command**: Calls `salsa/files/index.ts` (`files_listAll`).
41
+ 3. **Salsa**: Calls `cumin` to fetch data from CUBE.
42
+ 4. **Salsa**: Returns raw data to Command.
43
+ 5. **Command**: Maps raw data to `ListingItem[]` Model. Returns Model.
44
+ 6. **CLI/Shell**: Passes `ListingItem[]` to `chili/views/ls.ts` (`renderGrid` or `renderLong`).
45
+ 7. **View**: Returns formatted string.
46
+ 8. **CLI/Shell**: Prints string to stdout.
47
+
48
+ This ensures `chell` and `chili` behave identically.
@@ -0,0 +1,84 @@
1
+ = Architecture of Context-Driven Resource Resolution
2
+ :toc: left
3
+ :toclevels: 3
4
+
5
+ This document details the core architectural pattern that powers ChILI and Salsa: **Context-Driven Resource Resolution**. This mechanism allows the application to dynamically resolve and interact with nested API resources based on a persistent session state, decoupling the CLI commands from the hierarchical structure of the REST API.
6
+
7
+ == The Challenge
8
+
9
+ The ChRIS REST API is hierarchical. To access a specific resource, you often need to traverse a path:
10
+ `GET /api/v1/plugins/14/parameters/`
11
+
12
+ In a traditional CLI, this would require repetitive arguments:
13
+ `chili parameters list --plugin 14`
14
+
15
+ If a user is performing multiple operations on Plugin 14, repeating `--plugin 14` is tedious. Furthermore, different resources (Files, Feed Notes, Plugin Instances) share this "child-of-parent" pattern but have completely different API endpoints and types. Hardcoding handlers for each would violate DRY (Don't Repeat Yourself) principles.
16
+
17
+ == The Solution: Dynamic Object Contexts
18
+
19
+ We solve this by abstracting the interaction into three layers:
20
+
21
+ 1. **State Persistence (`chrisContext`)**: Stores the "ID" or "Path" of the current parent object (e.g., `plugin:14`).
22
+ 2. **The Registry (`ObjContexts`)**: Maps abstract resource names to API methods.
23
+ 3. **The Factory (`objContext_create`)**: Dynamically instantiates a resource manager for the current state.
24
+
25
+ === 1. The Registry (`ObjContexts`)
26
+
27
+ Located in `cumin/src/resources/chrisObjContext.ts`, this registry defines the "universe" of context-aware resources. It maps a generic `type` string to the specific method required to fetch that collection from a parent.
28
+
29
+ [source,typescript]
30
+ ----
31
+ const ObjContexts = {
32
+ ParametersOfPlugin: {
33
+ name: "ParametersOfPlugin",
34
+ getMethod: "getPluginParameters", // The API method to call on the parent
35
+ contextType: "plugin", // The type of parent required
36
+ },
37
+ ChRISFilesContext: {
38
+ name: "Files",
39
+ getMethod: "getFiles",
40
+ contextType: "folder",
41
+ },
42
+ // ...
43
+ };
44
+ ----
45
+
46
+ === 2. The Factory (`objContext_create`)
47
+
48
+ This function acts as the bridge between the persistent state and the live API.
49
+
50
+ 1. It accepts a **Type** (e.g., `"ParametersOfPlugin"`) and a **Context String** (e.g., `"plugin:14"`).
51
+ 2. It parses the context string to extract the ID (`14`).
52
+ 3. It fetches the *Parent Object* (Plugin 14) from the API.
53
+ 4. It returns a `ChRISEmbeddedResourceGroup` bound to that parent.
54
+
55
+ This generic group object exposes standard methods (`list`, `search`) that the CLI handlers consume, completely unaware of whether they are listing "files in a folder" or "parameters of a plugin."
56
+
57
+ == Data Flow Example
58
+
59
+ When a user runs:
60
+ `chili parametersofplugin list`
61
+
62
+ The flow is:
63
+
64
+ 1. **CLI Handler**: `PluginContextGroupHandler` is invoked.
65
+ 2. **Context Lookup**: It asks `chrisContext`: "What is the current plugin?" -> Returns `14`.
66
+ 3. **Controller**: `PluginContextController` calls the factory:
67
+ `objContext_create("ParametersOfPlugin", "plugin:14")`
68
+ 4. **Factory**:
69
+ * Looks up `ParametersOfPlugin` in registry.
70
+ * Fetches Plugin `14`.
71
+ * Returns a group object bound to `plugin.getPluginParameters()`.
72
+ 5. **BaseHandler**: Calls `.list()` on the group and displays the results.
73
+
74
+ == Extensibility
75
+
76
+ This architecture allows for rapid expansion. To support a new nested resource (e.g., "Comments on a Feed"):
77
+
78
+ 1. Add an entry to `ObjContexts` in `cumin`:
79
+ ```typescript
80
+ FeedComments: { getMethod: 'getComments', contextType: 'feed' }
81
+ ```
82
+ 2. Register a command in `chili` that calls the factory with `"FeedComments"`.
83
+
84
+ No new logic for listing, filtering, or displaying data is required. The system handles it generically.
@@ -0,0 +1,42 @@
1
+ = ChILI Project Roadmap & Architecture Vision
2
+ :toc: left
3
+ :toclevels: 2
4
+ :doctype: book
5
+
6
+ == The Sandwich Model: Architecture Overview
7
+
8
+ The ChRIS Interactive Line Interface (ChILI) project is evolving towards a robust, layered architecture, often referred to as the "Sandwich Model" or Clean Architecture. This design ensures modularity, testability, and reusability across various platforms (CLI, Web, Mobile).
9
+
10
+ === Layers of the Sandwich
11
+
12
+ * *Top Layer (Interface/Presentation)*:
13
+ **`chili`**: The Command Line Interface (CLI) application itself. Its primary responsibility is to parse user input (commands, flags), format output for the console, and dispatch actions to the underlying logic layer. It is the CLI analogue of a React or Angular frontend.
14
+ **`chell` (Future)**: An interactive REPL (Read-Eval-Print Loop) providing a shell-like experience to ChRIS users. It will offer Unix-like commands (`ls`, `cd`, `pwd`) and interactive sessions. This is a future evolution of `chili`.
15
+
16
+ * *Middle Layer (Application Logic/Services)*:
17
+ **`salsa`**: Shared Application Logic and Service Assets. This layer encapsulates the high-level business logic and "intents" (e.g., `feed_create`, `plugin_run`, `files_getGroup`). It acts as a platform-agnostic facade, abstracting away the intricacies of interacting with the ChRIS backend. `salsa` is designed to be reusable by `chili` (and `chell`), web frontends, or any other application consuming ChRIS functionality.
18
+
19
+ * *Bottom Layer (Infrastructure/Primitives)*:
20
+ **`cumin`**: A utility library providing lower-level primitives, state management (token handling, context), and a high-level, object-oriented API for interacting with the ChRIS system. `cumin` abstracts away the direct API calls.
21
+ **`@fnndsc/chrisapi`**: The foundational, low-level client library that makes direct REST API calls to the ChRIS server. `cumin` wraps and manages this client.
22
+
23
+ This layered approach enables independent development and evolution of each part, making the system more maintainable and adaptable.
24
+
25
+ == Future Vision: Chell - The ChRIS Holistic Layer of Logic
26
+
27
+ Our ultimate goal is to evolve the ChILI experience into a full-fledged interactive shell, tentatively named *Chell*.
28
+
29
+ === Chell's Purpose
30
+
31
+ * **Holistic Interaction**: Provide a seamless and intuitive environment for users to manage ChRIS resources.
32
+ * **Unix-like Familiarity**: Offer commands that feel natural to users accustomed to shell environments (`ls`, `cd`, `pwd`, `cp`, `mv`).
33
+ * **Interactive Context**: Maintain session state, allowing users to navigate the ChRIS filesystem and interact with resources dynamically.
34
+
35
+ === Role of 'chefs'
36
+
37
+ The newly created `chefs` module within `chili` is the initial step towards realizing the *Chell* vision. It serves as the primitive layer for shell-like filesystem commands:
38
+
39
+ * **`chili chefs ls`**: Provides a `ls`-like output for ChRIS files and directories.
40
+ * **`chili chefs mkdir`**: Future command for creating directories (e.g., within feeds).
41
+
42
+ `chefs` directly utilizes `salsa`'s high-level file management functions, demonstrating how `salsa` serves as the core logic provider for even specialized interface layers like a shell.