@@ -22,7 +22,8 @@ const _path = require('path');
2222const { Context } = require ( "../../../util/context" ) ;
2323const { v4 : uuidv4 } = require ( 'uuid' ) ;
2424const config = require ( "../../../config" ) ;
25- const { try_infer_attributes, NodeChildSelector, NodePathSelector, NodeSelector } = require ( "../../../filesystem/node/selectors" ) ;
25+ const { try_infer_attributes, NodeChildSelector, NodePathSelector, NodeUIDSelector, NodeSelector } = require ( "../../../filesystem/node/selectors" ) ;
26+ const fsCapabilities = require ( "../../../filesystem/definitions/capabilities" ) ;
2627
2728const path = require ( 'path' ) ;
2829const APIError = require ( "../../../api/APIError" ) ;
@@ -62,11 +63,10 @@ class MemoryFile {
6263 this . is_shortcut = 0 ;
6364 this . is_symlink = 0 ;
6465 this . symlink_path = null ;
65- this . created = 123 ;
66- this . accessed = 123 ;
67- this . modified = 123 ;
68-
69- this . own
66+ this . created = Math . floor ( Date . now ( ) / 1000 ) ;
67+ this . accessed = Math . floor ( Date . now ( ) / 1000 ) ;
68+ this . modified = Math . floor ( Date . now ( ) / 1000 ) ;
69+ this . size = is_dir ? 0 : ( content ? content . length : 0 ) ;
7070 }
7171}
7272
@@ -94,6 +94,20 @@ class MemoryFSProvider {
9494 this . entriesByUUID . set ( root . uuid , root ) ;
9595 }
9696
97+ /**
98+ * Get the capabilities of this filesystem provider.
99+ *
100+ * @returns {Set } - Set of capabilities supported by this provider.
101+ */
102+ get_capabilities ( ) {
103+ return new Set ( [
104+ fsCapabilities . READDIR_UUID_MODE ,
105+ fsCapabilities . UUID ,
106+ fsCapabilities . READ ,
107+ fsCapabilities . WRITE ,
108+ ] ) ;
109+ }
110+
97111 /**
98112 * Normalize the path to be relative to the mountpoint. Returns `/` if the path is empty/undefined.
99113 *
@@ -119,7 +133,7 @@ class MemoryFSProvider {
119133 /**
120134 * Check the integrity of the whole memory filesystem and the input. Throws error if any violation is found.
121135 *
122- * @param {MemoryFile } entry - The entry to check.
136+ * @param {MemoryFile|FSNodeContext } entry - The entry to check.
123137 * @returns {Promise<void> }
124138 */
125139 _integrity_check ( entry ) {
@@ -130,7 +144,8 @@ class MemoryFSProvider {
130144
131145 if ( entry ) {
132146 // check all directories along the path are valid
133- const inner_path = this . _inner_path ( entry . path ) ;
147+ const path_to_check = 'entry' in entry ? entry . entry ?. path : entry . path ;
148+ const inner_path = this . _inner_path ( path_to_check ) ;
134149 const path_components = inner_path . split ( '/' ) ;
135150 for ( let i = 2 ; i < path_components . length ; i ++ ) {
136151 const path_component = path_components . slice ( 0 , i ) . join ( '/' ) ;
@@ -211,6 +226,33 @@ class MemoryFSProvider {
211226 return entry ;
212227 }
213228
229+ /**
230+ * Read directory contents.
231+ *
232+ * @param {Object } param
233+ * @param {Context } param.context - The context of the operation.
234+ * @param {FSNodeContext } param.node - The directory node to read.
235+ * @returns {Promise<string[]> } - Array of child UUIDs.
236+ */
237+ async readdir ( { context, node } ) {
238+ const inner_path = this . _inner_path ( node . path ) ;
239+ const child_uuids = [ ] ;
240+
241+ // Find all entries that are direct children of this directory
242+ for ( const [ path , uuid ] of this . entriesByPath ) {
243+ if ( path === inner_path ) {
244+ continue ; // Skip the directory itself
245+ }
246+
247+ const dirname = _path . dirname ( path ) ;
248+ if ( dirname === inner_path ) {
249+ child_uuids . push ( uuid ) ;
250+ }
251+ }
252+
253+ return child_uuids ;
254+ }
255+
214256 /**
215257 * Create a new directory.
216258 *
@@ -301,7 +343,7 @@ class MemoryFSProvider {
301343 const new_inner_path = this . _inner_path ( new_full_path ) ;
302344 const entry = new MemoryFile ( {
303345 full_path : new_full_path ,
304- is_dir : false ,
346+ is_dir : node . entry . is_dir ,
305347 content : node . entry . content ,
306348 } ) ;
307349 entry . uuid = node . entry . uuid ;
@@ -318,6 +360,49 @@ class MemoryFSProvider {
318360 return entry ;
319361 }
320362
363+ /**
364+ * Copy a tree of files and directories.
365+ *
366+ * @param {Object } param
367+ * @param {Context } param.context
368+ * @param {FSNodeContext } param.source - The source node to copy.
369+ * @param {FSNodeContext } param.parent - The parent directory for the copy.
370+ * @param {string } param.target_name - The name for the copied item.
371+ * @returns {Promise<FSNodeContext> } - The copied node.
372+ */
373+ async copy_tree ( { context, source, parent, target_name } ) {
374+ const fs = context . get ( 'services' ) . get ( 'filesystem' ) ;
375+
376+ if ( source . entry . is_dir ) {
377+ // Create the directory
378+ const new_dir = await this . mkdir ( { context, parent, name : target_name } ) ;
379+
380+ // Copy all children
381+ const children = await this . readdir ( { context, node : source } ) ;
382+ for ( const child_uuid of children ) {
383+ const child_node = await fs . node ( new NodeUIDSelector ( child_uuid ) ) ;
384+ const child_name = child_node . entry . name ;
385+ await this . copy_tree ( {
386+ context,
387+ source : child_node ,
388+ parent : new_dir ,
389+ target_name : child_name
390+ } ) ;
391+ }
392+
393+ return new_dir ;
394+ } else {
395+ // Copy the file
396+ const new_file = await this . write_new ( {
397+ context,
398+ parent,
399+ name : target_name ,
400+ file : { stream : { read : ( ) => source . entry . content } }
401+ } ) ;
402+ return new_file ;
403+ }
404+ }
405+
321406 /**
322407 * Write a new file to the filesystem. Throws an error if the destination
323408 * already exists.
@@ -336,15 +421,14 @@ class MemoryFSProvider {
336421 const entry = new MemoryFile ( {
337422 full_path : full_path ,
338423 is_dir : false ,
339- content : file . stream . readableBuffer ,
424+ content : file . stream . read ( ) ,
340425 } ) ;
341426 this . entriesByPath . set ( inner_path , entry . uuid ) ;
342427 this . entriesByUUID . set ( entry . uuid , entry ) ;
343428
344429 const fs = context . get ( 'services' ) . get ( 'filesystem' ) ;
345430 const node = await fs . node ( entry . path ) ;
346-
347- this . _integrity_check ( node ) ;
431+ await node . fetchEntry ( ) ;
348432
349433 return node ;
350434 }
@@ -371,12 +455,19 @@ class MemoryFSProvider {
371455 throw new Error ( `Cannot overwrite a directory` ) ;
372456 }
373457
374- original_entry . content = file . stream . readableBuffer ;
458+ original_entry . content = file . stream . read ( ) ;
459+ original_entry . modified = Math . floor ( Date . now ( ) / 1000 ) ;
460+ original_entry . size = original_entry . content ? original_entry . content . length : 0 ;
375461 this . entriesByUUID . set ( node . uid , original_entry ) ;
376462 }
377463
378464 this . _integrity_check ( node ) ;
379465
466+ // return node;
467+ const fs = context . get ( 'services' ) . get ( 'filesystem' ) ;
468+ node = await fs . node ( original_entry . path ) ;
469+ await node . fetchEntry ( ) ;
470+
380471 return node ;
381472 }
382473}
0 commit comments