@procedures/fs 0.0.0-stage → 0.1.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +895 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +17 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/module.d.ts +16 -0
  10. package/dist/module.d.ts.map +1 -0
  11. package/dist/module.js +178 -0
  12. package/dist/module.js.map +1 -0
  13. package/dist/procedures/fs/copy.d.ts +11 -0
  14. package/dist/procedures/fs/copy.d.ts.map +1 -0
  15. package/dist/procedures/fs/copy.js +21 -0
  16. package/dist/procedures/fs/copy.js.map +1 -0
  17. package/dist/procedures/fs/exists.d.ts +11 -0
  18. package/dist/procedures/fs/exists.d.ts.map +1 -0
  19. package/dist/procedures/fs/exists.js +20 -0
  20. package/dist/procedures/fs/exists.js.map +1 -0
  21. package/dist/procedures/fs/glob.d.ts +17 -0
  22. package/dist/procedures/fs/glob.d.ts.map +1 -0
  23. package/dist/procedures/fs/glob.js +85 -0
  24. package/dist/procedures/fs/glob.js.map +1 -0
  25. package/dist/procedures/fs/index.d.ts +12 -0
  26. package/dist/procedures/fs/index.d.ts.map +1 -0
  27. package/dist/procedures/fs/index.js +12 -0
  28. package/dist/procedures/fs/index.js.map +1 -0
  29. package/dist/procedures/fs/json.d.ts +11 -0
  30. package/dist/procedures/fs/json.d.ts.map +1 -0
  31. package/dist/procedures/fs/json.js +16 -0
  32. package/dist/procedures/fs/json.js.map +1 -0
  33. package/dist/procedures/fs/mkdir.d.ts +11 -0
  34. package/dist/procedures/fs/mkdir.d.ts.map +1 -0
  35. package/dist/procedures/fs/mkdir.js +39 -0
  36. package/dist/procedures/fs/mkdir.js.map +1 -0
  37. package/dist/procedures/fs/move.d.ts +11 -0
  38. package/dist/procedures/fs/move.d.ts.map +1 -0
  39. package/dist/procedures/fs/move.js +46 -0
  40. package/dist/procedures/fs/move.js.map +1 -0
  41. package/dist/procedures/fs/read.d.ts +11 -0
  42. package/dist/procedures/fs/read.d.ts.map +1 -0
  43. package/dist/procedures/fs/read.js +21 -0
  44. package/dist/procedures/fs/read.js.map +1 -0
  45. package/dist/procedures/fs/readdir.d.ts +11 -0
  46. package/dist/procedures/fs/readdir.d.ts.map +1 -0
  47. package/dist/procedures/fs/readdir.js +38 -0
  48. package/dist/procedures/fs/readdir.js.map +1 -0
  49. package/dist/procedures/fs/rm.d.ts +11 -0
  50. package/dist/procedures/fs/rm.d.ts.map +1 -0
  51. package/dist/procedures/fs/rm.js +30 -0
  52. package/dist/procedures/fs/rm.js.map +1 -0
  53. package/dist/procedures/fs/stat.d.ts +11 -0
  54. package/dist/procedures/fs/stat.d.ts.map +1 -0
  55. package/dist/procedures/fs/stat.js +29 -0
  56. package/dist/procedures/fs/stat.js.map +1 -0
  57. package/dist/procedures/fs/write.d.ts +11 -0
  58. package/dist/procedures/fs/write.d.ts.map +1 -0
  59. package/dist/procedures/fs/write.js +24 -0
  60. package/dist/procedures/fs/write.js.map +1 -0
  61. package/dist/procedures/fs.d.ts +5 -0
  62. package/dist/procedures/fs.d.ts.map +1 -0
  63. package/dist/procedures/fs.js +5 -0
  64. package/dist/procedures/fs.js.map +1 -0
  65. package/dist/register.d.ts +8 -0
  66. package/dist/register.d.ts.map +1 -0
  67. package/dist/register.js +14 -0
  68. package/dist/register.js.map +1 -0
  69. package/dist/types.d.ts +187 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +197 -0
  72. package/dist/types.js.map +1 -0
  73. package/package.json +74 -3
  74. package/src/index.ts +17 -0
  75. package/src/module.ts +238 -0
  76. package/src/procedures/fs/copy.ts +24 -0
  77. package/src/procedures/fs/exists.ts +20 -0
  78. package/src/procedures/fs/glob.ts +92 -0
  79. package/src/procedures/fs/index.ts +11 -0
  80. package/src/procedures/fs/json.ts +21 -0
  81. package/src/procedures/fs/mkdir.ts +43 -0
  82. package/src/procedures/fs/move.ts +47 -0
  83. package/src/procedures/fs/read.ts +23 -0
  84. package/src/procedures/fs/readdir.ts +44 -0
  85. package/src/procedures/fs/rm.ts +31 -0
  86. package/src/procedures/fs/stat.ts +30 -0
  87. package/src/procedures/fs/write.ts +27 -0
  88. package/src/procedures/fs.ts +5 -0
  89. package/src/register.ts +17 -0
  90. package/src/types.ts +407 -0
package/README.md CHANGED
@@ -1,3 +1,896 @@
1
- # Temporary Holding Version
1
+ # @procedures/fs
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/@procedures/fs?color=2346c4&label=npm)](https://www.npmjs.com/package/@procedures/fs)
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-22+-green.svg)](https://nodejs.org/)
8
+
9
+ File system operations as RPC procedures. Read, write, copy, move, glob files and directories via `client.call()`.
10
+
11
+ ## Overview
12
+
13
+ `client-fs` provides comprehensive file system access through the client procedure ecosystem with:
14
+ - **File operations**: Read, write, stat, exists, remove
15
+ - **Directory operations**: Create, list, copy, move
16
+ - **Pattern matching**: Glob file search via the Node.js `fs.glob` API (`node:fs/promises`, requires Node >= 22)
17
+ - **JSON utilities**: Read and parse JSON files
18
+ - **Zod validation**: Runtime type safety on all inputs
19
+
20
+ The package auto-registers 11 procedures:
21
+ `fs.read`, `fs.write`, `fs.exists`, `fs.stat`, `fs.mkdir`, `fs.rm`, `fs.readdir`, `fs.copy`, `fs.move`, `fs.glob`, `fs.read.json`
22
+
23
+ ## Architecture
24
+
25
+ ```mermaid
26
+ graph TB
27
+ subgraph "Client Ecosystem"
28
+ Client["@procedures/client"]
29
+ Registry["Procedure Registry"]
30
+ end
31
+
32
+ subgraph "client-fs Package"
33
+ Register["register.ts<br/>(Auto-registers procedures)"]
34
+ Types["types.ts<br/>(Zod Schemas + Types)"]
35
+
36
+ subgraph "Procedure Categories"
37
+ FileOps["File Operations<br/>read, write, exists, stat, rm"]
38
+ DirOps["Directory Operations<br/>mkdir, readdir, copy, move"]
39
+ PatternOps["Pattern Operations<br/>glob, read.json"]
40
+ end
41
+ end
42
+
43
+ subgraph "Node.js Modules"
44
+ NodeFs["node:fs/promises"]
45
+ NodePath["node:path"]
46
+ NodeGlob["node:fs/promises glob<br/>(Node >= 22)"]
47
+ end
48
+
49
+ subgraph "File System"
50
+ FS["Local File System"]
51
+ end
52
+
53
+ Register --> Client
54
+ Register --> Registry
55
+
56
+ FileOps --> NodeFs
57
+ DirOps --> NodeFs
58
+ PatternOps --> NodeGlob
59
+
60
+ NodeFs --> NodePath
61
+ NodeFs --> FS
62
+ NodeGlob --> FS
63
+
64
+ Types -.validates.-> FileOps
65
+ Types -.validates.-> DirOps
66
+ Types -.validates.-> PatternOps
67
+
68
+ style Register fill:#e1f5ff
69
+ style Types fill:#f0f0f0
70
+ ```
71
+
72
+ ### Package Dependency Flow
73
+
74
+ ```mermaid
75
+ graph LR
76
+ App["Your Application"]
77
+ ClientFs["@procedures/fs"]
78
+ Client["@procedures/client"]
79
+ Zod["zod"]
80
+ NodeModules["node:fs/promises<br/>(glob: Node >= 22)"]
81
+
82
+ App --> ClientFs
83
+ ClientFs --> Client
84
+ ClientFs --> Zod
85
+ ClientFs --> NodeModules
86
+
87
+ App -.client.call.-> Client
88
+ Client -.executes.-> ClientFs
89
+ ClientFs -.validates.-> Zod
90
+ ClientFs -.file ops.-> NodeModules
91
+ ```
92
+
93
+ ## Installation
94
+
95
+ ```bash
96
+ npm install @procedures/fs
97
+ ```
98
+
99
+ ### Peer Dependencies
100
+
101
+ ```json
102
+ {
103
+ "@procedures/client": "github:mark1russell7/client#main",
104
+ "zod": "^3.24.0"
105
+ }
106
+ ```
107
+
108
+ ## API Reference
109
+
110
+ ### File Operations
111
+
112
+ #### `fs.read`
113
+
114
+ Read file contents as a string.
115
+
116
+ ```typescript
117
+ const result = await client.exec(
118
+ ["fs", "read"],
119
+ {
120
+ path: "./config.json",
121
+ encoding: "utf8"
122
+ }
123
+ );
124
+
125
+ console.log(result.content);
126
+ console.log(result.stats.size);
127
+ ```
128
+
129
+ **Input:**
130
+ ```typescript
131
+ interface ReadInput {
132
+ path: string; // File path (absolute or relative)
133
+ encoding?: string; // Default: "utf8"
134
+ }
135
+ ```
136
+
137
+ **Output:**
138
+ ```typescript
139
+ interface ReadOutput {
140
+ content: string; // File contents
141
+ path: string; // Resolved absolute path
142
+ stats: StatOutput; // File statistics
143
+ }
144
+ ```
145
+
146
+ #### `fs.write`
147
+
148
+ Write content to a file.
149
+
150
+ ```typescript
151
+ await client.exec(
152
+ ["fs", "write"],
153
+ {
154
+ path: "./output.txt",
155
+ content: "Hello, World!",
156
+ mode: "write" // or "append"
157
+ }
158
+ );
159
+ ```
160
+
161
+ **Input:**
162
+ ```typescript
163
+ interface WriteInput {
164
+ path: string; // File path
165
+ content: string; // Content to write
166
+ encoding?: string; // Default: "utf8"
167
+ mode?: "write" | "append"; // Default: "write"
168
+ }
169
+ ```
170
+
171
+ **Output:**
172
+ ```typescript
173
+ interface WriteOutput {
174
+ path: string; // Written file path
175
+ bytesWritten: number; // Number of bytes written
176
+ }
177
+ ```
178
+
179
+ #### `fs.exists`
180
+
181
+ Check if a path exists and get its type.
182
+
183
+ ```typescript
184
+ const result = await client.exec(
185
+ ["fs", "exists"],
186
+ { path: "./my-file.txt" }
187
+ );
188
+
189
+ if (result.exists) {
190
+ console.log(`Path exists and is a ${result.stats.type}`);
191
+ }
192
+ ```
193
+
194
+ **Input:**
195
+ ```typescript
196
+ interface ExistsInput {
197
+ path: string;
198
+ }
199
+ ```
200
+
201
+ **Output:**
202
+ ```typescript
203
+ type ExistsOutput =
204
+ | { exists: false }
205
+ | { exists: true; stats: StatOutput };
206
+ ```
207
+
208
+ #### `fs.stat`
209
+
210
+ Get file or directory statistics.
211
+
212
+ ```typescript
213
+ const result = await client.exec(
214
+ ["fs", "stat"],
215
+ { path: "./my-directory" }
216
+ );
217
+
218
+ console.log(result.type); // "file" | "directory" | "other"
219
+ console.log(result.size); // Size in bytes
220
+ ```
221
+
222
+ **Input:**
223
+ ```typescript
224
+ interface StatInput {
225
+ path: string;
226
+ }
227
+ ```
228
+
229
+ **Output:**
230
+ ```typescript
231
+ interface StatOutput {
232
+ path: string;
233
+ type: FileType; // "file" | "directory" | "other"
234
+ size: number; // Size in bytes
235
+ mtime: string; // Modified time (ISO)
236
+ ctime: string; // Created time (ISO)
237
+ atime: string; // Accessed time (ISO)
238
+ mode: number; // Unix permissions
239
+ }
240
+
241
+ enum FileType {
242
+ File = "file",
243
+ Directory = "directory",
244
+ Other = "other"
245
+ }
246
+ ```
247
+
248
+ #### `fs.rm`
249
+
250
+ Remove a file or directory.
251
+
252
+ ```typescript
253
+ // Remove file
254
+ await client.exec(
255
+ ["fs", "rm"],
256
+ { path: "./old-file.txt" }
257
+ );
258
+
259
+ // Remove directory recursively
260
+ await client.exec(
261
+ ["fs", "rm"],
262
+ {
263
+ path: "./old-directory",
264
+ recursive: true,
265
+ force: true
266
+ }
267
+ );
268
+ ```
269
+
270
+ **Input:**
271
+ ```typescript
272
+ interface RmInput {
273
+ path: string;
274
+ recursive?: boolean; // Default: false
275
+ force?: boolean; // Ignore if doesn't exist (default: false)
276
+ }
277
+ ```
278
+
279
+ **Output:**
280
+ ```typescript
281
+ interface RmOutput {
282
+ path: string;
283
+ removed: boolean; // false if didn't exist with force=true
284
+ }
285
+ ```
286
+
287
+ ### Directory Operations
288
+
289
+ #### `fs.mkdir`
290
+
291
+ Create a directory.
292
+
293
+ ```typescript
294
+ await client.exec(
295
+ ["fs", "mkdir"],
296
+ {
297
+ path: "./new/nested/directory",
298
+ recursive: true // Create parents if needed
299
+ }
300
+ );
301
+ ```
302
+
303
+ **Input:**
304
+ ```typescript
305
+ interface MkdirInput {
306
+ path: string;
307
+ recursive?: boolean; // Default: true
308
+ }
309
+ ```
310
+
311
+ **Output:**
312
+ ```typescript
313
+ interface MkdirOutput {
314
+ path: string;
315
+ created: boolean; // false if already existed
316
+ }
317
+ ```
318
+
319
+ #### `fs.readdir`
320
+
321
+ List directory contents.
322
+
323
+ ```typescript
324
+ const result = await client.exec(
325
+ ["fs", "readdir"],
326
+ {
327
+ path: "./src",
328
+ recursive: true,
329
+ includeStats: true
330
+ }
331
+ );
332
+
333
+ result.entries.forEach(entry => {
334
+ console.log(`${entry.type}: ${entry.path}`);
335
+ if (entry.stats) {
336
+ console.log(` Size: ${entry.stats.size} bytes`);
337
+ }
338
+ });
339
+ ```
340
+
341
+ **Input:**
342
+ ```typescript
343
+ interface ReaddirInput {
344
+ path: string;
345
+ recursive?: boolean; // Default: false
346
+ includeStats?: boolean; // Default: false
347
+ }
348
+ ```
349
+
350
+ **Output:**
351
+ ```typescript
352
+ interface ReaddirOutput {
353
+ path: string;
354
+ entries: ReaddirEntry[];
355
+ }
356
+
357
+ interface ReaddirEntry {
358
+ name: string; // Entry name
359
+ path: string; // Full path
360
+ type: FileType; // "file" | "directory" | "other"
361
+ stats?: StatOutput; // If includeStats=true
362
+ }
363
+ ```
364
+
365
+ #### `fs.copy`
366
+
367
+ Copy a file or directory.
368
+
369
+ ```typescript
370
+ // Copy file
371
+ await client.exec(
372
+ ["fs", "copy"],
373
+ {
374
+ src: "./source.txt",
375
+ dest: "./destination.txt",
376
+ overwrite: true
377
+ }
378
+ );
379
+
380
+ // Copy directory recursively
381
+ await client.exec(
382
+ ["fs", "copy"],
383
+ {
384
+ src: "./src-dir",
385
+ dest: "./dest-dir",
386
+ recursive: true
387
+ }
388
+ );
389
+ ```
390
+
391
+ **Input:**
392
+ ```typescript
393
+ interface CopyInput {
394
+ src: string;
395
+ dest: string;
396
+ recursive?: boolean; // Default: true
397
+ overwrite?: boolean; // Default: false
398
+ }
399
+ ```
400
+
401
+ **Output:**
402
+ ```typescript
403
+ interface CopyOutput {
404
+ src: string;
405
+ dest: string;
406
+ }
407
+ ```
408
+
409
+ #### `fs.move`
410
+
411
+ Move or rename a file or directory.
412
+
413
+ ```typescript
414
+ await client.exec(
415
+ ["fs", "move"],
416
+ {
417
+ src: "./old-name.txt",
418
+ dest: "./new-name.txt",
419
+ overwrite: false
420
+ }
421
+ );
422
+ ```
423
+
424
+ **Input:**
425
+ ```typescript
426
+ interface MoveInput {
427
+ src: string;
428
+ dest: string;
429
+ overwrite?: boolean; // Default: false
430
+ }
431
+ ```
432
+
433
+ **Output:**
434
+ ```typescript
435
+ interface MoveOutput {
436
+ src: string;
437
+ dest: string;
438
+ }
439
+ ```
440
+
441
+ ### Pattern Matching Operations
442
+
443
+ #### `fs.glob`
444
+
445
+ Find files matching a glob pattern using the Node.js `fs.glob` API (`node:fs/promises`, requires Node >= 22).
446
+
447
+ ```typescript
448
+ const result = await client.exec(
449
+ ["fs", "glob"],
450
+ {
451
+ pattern: "src/**/*.{ts,tsx}",
452
+ cwd: "/my/project",
453
+ absolute: true,
454
+ dot: false
455
+ }
456
+ );
457
+
458
+ console.log(result.matches);
459
+ // ["/my/project/src/index.ts", "/my/project/src/components/App.tsx", ...]
460
+ ```
461
+
462
+ **Input:**
463
+ ```typescript
464
+ interface GlobInput {
465
+ pattern: string; // Glob pattern (e.g., "**/*.ts")
466
+ cwd?: string; // Base directory (default: process.cwd())
467
+ absolute?: boolean; // Return absolute paths (default: false)
468
+ dot?: boolean; // Include dotfiles (default: false)
469
+ }
470
+ ```
471
+
472
+ **Output:**
473
+ ```typescript
474
+ interface GlobOutput {
475
+ pattern: string;
476
+ matches: string[]; // Matching file paths
477
+ }
478
+ ```
479
+
480
+ **Glob Pattern Examples:**
481
+ ```typescript
482
+ // All TypeScript files
483
+ "**/*.ts"
484
+
485
+ // All files in src directory
486
+ "src/**/*"
487
+
488
+ // TypeScript and JavaScript files
489
+ "**/*.{ts,js}"
490
+
491
+ // Note: node:fs/promises glob does NOT exclude node_modules by default;
492
+ // pass an `exclude` pattern or scope the pattern (e.g. "src/**/*.ts")
493
+ "src/**/*.ts"
494
+
495
+ // Include test files
496
+ "**/*.{test,spec}.ts"
497
+ ```
498
+
499
+ #### `fs.read.json`
500
+
501
+ Read and parse a JSON file.
502
+
503
+ ```typescript
504
+ const result = await client.exec(
505
+ ["fs", "read.json"],
506
+ { path: "./package.json" }
507
+ );
508
+
509
+ console.log(result.data); // Parsed JSON object
510
+ ```
511
+
512
+ **Input:**
513
+ ```typescript
514
+ interface ReadJsonInput {
515
+ path: string;
516
+ }
517
+ ```
518
+
519
+ **Output:**
520
+ ```typescript
521
+ interface ReadJsonOutput {
522
+ path: string;
523
+ data: unknown; // Parsed JSON (cast to your type)
524
+ stats: StatOutput;
525
+ }
526
+ ```
527
+
528
+ **Type-safe example:**
529
+ ```typescript
530
+ interface PackageJson {
531
+ name: string;
532
+ version: string;
533
+ dependencies: Record<string, string>;
534
+ }
535
+
536
+ const result = await client.exec(
537
+ ["fs", "read.json"],
538
+ { path: "./package.json" }
539
+ );
540
+
541
+ const pkg = result.data as PackageJson;
542
+ console.log(pkg.name, pkg.version);
543
+ ```
544
+
545
+ ## Usage Examples
546
+
547
+ ### Basic File Operations
548
+
549
+ ```typescript
550
+ import { Client, LocalTransport, PROCEDURE_REGISTRY } from "@procedures/client";
551
+ import "@procedures/fs"; // registers its procedures
552
+
553
+ const client = new Client(new LocalTransport({ registry: PROCEDURE_REGISTRY }));
554
+
555
+ // Read a file
556
+ const { content } = await client.exec(
557
+ ["fs", "read"],
558
+ { path: "./README.md" }
559
+ );
560
+
561
+ // Write to a file
562
+ await client.exec(
563
+ ["fs", "write"],
564
+ {
565
+ path: "./output.txt",
566
+ content: "Generated content\n",
567
+ mode: "write"
568
+ }
569
+ );
570
+
571
+ // Append to a file
572
+ await client.exec(
573
+ ["fs", "write"],
574
+ {
575
+ path: "./log.txt",
576
+ content: `[${new Date().toISOString()}] Log entry\n`,
577
+ mode: "append"
578
+ }
579
+ );
580
+
581
+ // Check if file exists
582
+ const exists = await client.exec(
583
+ ["fs", "exists"],
584
+ { path: "./config.json" }
585
+ );
586
+
587
+ if (exists.exists) {
588
+ // Get file stats
589
+ const stats = await client.exec(
590
+ ["fs", "stat"],
591
+ { path: "./config.json" }
592
+ );
593
+ console.log(`File size: ${stats.size} bytes`);
594
+ }
595
+ ```
596
+
597
+ ### Directory Operations
598
+
599
+ ```typescript
600
+ // Create nested directories
601
+ await client.exec(
602
+ ["fs", "mkdir"],
603
+ {
604
+ path: "./build/assets/images",
605
+ recursive: true
606
+ }
607
+ );
608
+
609
+ // List directory contents
610
+ const { entries } = await client.exec(
611
+ ["fs", "readdir"],
612
+ {
613
+ path: "./src",
614
+ includeStats: true
615
+ }
616
+ );
617
+
618
+ entries.forEach(entry => {
619
+ if (entry.type === "file") {
620
+ console.log(`File: ${entry.name} (${entry.stats?.size} bytes)`);
621
+ } else {
622
+ console.log(`Directory: ${entry.name}`);
623
+ }
624
+ });
625
+
626
+ // Recursive directory listing
627
+ const { entries: allFiles } = await client.exec(
628
+ ["fs", "readdir"],
629
+ {
630
+ path: "./src",
631
+ recursive: true
632
+ }
633
+ );
634
+
635
+ console.log(`Total items: ${allFiles.length}`);
636
+ ```
637
+
638
+ ### Copy and Move Operations
639
+
640
+ ```typescript
641
+ // Backup a file
642
+ await client.exec(
643
+ ["fs", "copy"],
644
+ {
645
+ src: "./important-data.json",
646
+ dest: "./backups/important-data.backup.json"
647
+ }
648
+ );
649
+
650
+ // Copy entire directory
651
+ await client.exec(
652
+ ["fs", "copy"],
653
+ {
654
+ src: "./templates",
655
+ dest: "./build/templates",
656
+ recursive: true,
657
+ overwrite: true
658
+ }
659
+ );
660
+
661
+ // Rename a file
662
+ await client.exec(
663
+ ["fs", "move"],
664
+ {
665
+ src: "./old-name.txt",
666
+ dest: "./new-name.txt"
667
+ }
668
+ );
669
+
670
+ // Move to different directory
671
+ await client.exec(
672
+ ["fs", "move"],
673
+ {
674
+ src: "./temp/file.txt",
675
+ dest: "./archive/file.txt",
676
+ overwrite: false
677
+ }
678
+ );
679
+ ```
680
+
681
+ ### Pattern Matching with Glob
682
+
683
+ ```typescript
684
+ // Find all TypeScript files
685
+ const { matches: tsFiles } = await client.exec(
686
+ ["fs", "glob"],
687
+ {
688
+ pattern: "**/*.ts",
689
+ cwd: "./src",
690
+ absolute: true
691
+ }
692
+ );
693
+
694
+ console.log(`Found ${tsFiles.length} TypeScript files`);
695
+
696
+ // Find test files
697
+ const { matches: testFiles } = await client.exec(
698
+ ["fs", "glob"],
699
+ {
700
+ pattern: "**/*.{test,spec}.{ts,js}",
701
+ cwd: "."
702
+ }
703
+ );
704
+
705
+ // Find config files
706
+ const { matches: configs } = await client.exec(
707
+ ["fs", "glob"],
708
+ {
709
+ pattern: "**/*.config.{js,ts,json}",
710
+ dot: true // Include dotfiles like .eslintrc.json
711
+ }
712
+ );
713
+ ```
714
+
715
+ ### JSON File Operations
716
+
717
+ ```typescript
718
+ // Read package.json
719
+ const { data: pkg } = await client.exec(
720
+ ["fs", "read.json"],
721
+ { path: "./package.json" }
722
+ );
723
+
724
+ console.log(`Package: ${pkg.name}@${pkg.version}`);
725
+
726
+ // Read tsconfig.json
727
+ const { data: tsconfig } = await client.exec(
728
+ ["fs", "read.json"],
729
+ { path: "./tsconfig.json" }
730
+ );
731
+
732
+ console.log("Compiler options:", tsconfig.compilerOptions);
733
+
734
+ // Read and modify JSON
735
+ const { data: config } = await client.exec(
736
+ ["fs", "read.json"],
737
+ { path: "./config.json" }
738
+ );
739
+
740
+ config.lastUpdated = new Date().toISOString();
741
+
742
+ await client.exec(
743
+ ["fs", "write"],
744
+ {
745
+ path: "./config.json",
746
+ content: JSON.stringify(config, null, 2)
747
+ }
748
+ );
749
+ ```
750
+
751
+ ### Build Script Example
752
+
753
+ ```typescript
754
+ // Clean build directory
755
+ await client.exec(
756
+ ["fs", "rm"],
757
+ {
758
+ path: "./dist",
759
+ recursive: true,
760
+ force: true
761
+ }
762
+ );
763
+
764
+ // Create build directory
765
+ await client.exec(
766
+ ["fs", "mkdir"],
767
+ { path: "./dist" }
768
+ );
769
+
770
+ // Find all source files
771
+ const { matches: sourceFiles } = await client.exec(
772
+ ["fs", "glob"],
773
+ {
774
+ pattern: "src/**/*.ts",
775
+ absolute: true
776
+ }
777
+ );
778
+
779
+ // Process each file...
780
+ for (const file of sourceFiles) {
781
+ const { content } = await client.exec(
782
+ ["fs", "read"],
783
+ { path: file }
784
+ );
785
+
786
+ // Transform content...
787
+ const transformed = content.replace(/\.ts$/, ".js");
788
+
789
+ await client.exec(
790
+ ["fs", "write"],
791
+ {
792
+ path: file.replace("src/", "dist/").replace(".ts", ".js"),
793
+ content: transformed
794
+ }
795
+ );
796
+ }
797
+
798
+ // Copy assets
799
+ await client.exec(
800
+ ["fs", "copy"],
801
+ {
802
+ src: "./public",
803
+ dest: "./dist/public",
804
+ recursive: true
805
+ }
806
+ );
807
+ ```
808
+
809
+ ## Integration with Ecosystem
810
+
811
+ ### Bundle Packages
812
+
813
+ `client-fs` can be included in bundle packages:
814
+
815
+ ```typescript
816
+ // In bundle-dev/src/register.ts
817
+ import "@procedures/fs"; // registers its procedures
818
+ import "@procedures/shell"; // registers its procedures
819
+ import "@procedures/cli-run"; // registers its procedures
820
+ ```
821
+
822
+ ## Dependencies
823
+
824
+ ### Runtime Dependencies
825
+
826
+ - **@procedures/client** - Core client procedure system
827
+ - **zod** (^3.24.0) - Runtime type validation
828
+
829
+ ### Implicit Dependencies
830
+
831
+ - **node:fs/promises** - Node.js file system promises API
832
+ - **node:path** - Node.js path utilities
833
+ - **node:fs/promises `glob`** - Glob pattern matching (used by the `fs.glob` procedure; requires Node >= 22)
834
+
835
+ ### Development Dependencies
836
+
837
+ - **@mark1russell7/cue** - Configuration and type utilities
838
+ - **typescript** (^5.9.3) - TypeScript compiler
839
+ - **@types/node** (^22.0.0) - Node.js type definitions
840
+
841
+ ## Package Structure
842
+
843
+ ```
844
+ client-fs/
845
+ ├── src/
846
+ │ ├── types.ts # Zod schemas + TypeScript types
847
+ │ ├── register.ts # Auto-registration
848
+ │ ├── index.ts # Main entry point
849
+ │ ├── procedures/
850
+ │ │ ├── fs.ts # Barrel export
851
+ │ │ └── fs/
852
+ │ │ ├── index.ts # Procedure exports
853
+ │ │ ├── read.ts # fs.read
854
+ │ │ ├── write.ts # fs.write
855
+ │ │ ├── exists.ts # fs.exists
856
+ │ │ ├── stat.ts # fs.stat
857
+ │ │ ├── mkdir.ts # fs.mkdir
858
+ │ │ ├── rm.ts # fs.rm
859
+ │ │ ├── readdir.ts # fs.readdir
860
+ │ │ ├── copy.ts # fs.copy
861
+ │ │ ├── move.ts # fs.move
862
+ │ │ ├── glob.ts # fs.glob
863
+ │ │ └── json.ts # fs.read.json
864
+ ├── dist/ # Compiled output
865
+ ├── package.json
866
+ ├── tsconfig.json
867
+ └── README.md
868
+ ```
869
+
870
+ ## Development
871
+
872
+ ### Build
873
+
874
+ ```bash
875
+ npm run build
876
+ ```
877
+
878
+ ### Type Check
879
+
880
+ ```bash
881
+ npm run typecheck
882
+ ```
883
+
884
+ ### Clean
885
+
886
+ ```bash
887
+ npm run clean
888
+ ```
889
+
890
+ ## License
891
+
892
+ MIT
893
+
894
+ ## Author
895
+
896
+ Mark Russell <marktheprogrammer17@gmail.com>