touch-all 1.1.10 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # touch-all
2
2
 
3
- CLI tool to create folder structures from markdown tree representations.
3
+ CLI tool to create folder structures from Markdown tree representations.
4
4
 
5
- Pass a tree string — drawn with box-drawing characters or plain indentation — and `touch-all` creates every directory and file on disk.
5
+ ![](.media/terminal-sceen-cast.svg)
6
+
7
+ It behaves like `mkdir -p` and `touch` combined, creating directories and files as needed. It can be used to quickly scaffold a project structure or generate placeholder files.
6
8
 
7
9
  ## Features
8
10
 
9
11
  - Accepts tree strings in **box-drawing** (`├──`, `└──`, `│`) or **indentation** (spaces) format
10
- - Trailing `/` marks a directory; no trailing `/` marks a file
12
+ - Trailing slash `/` marks a directory; no trailing `/` marks a file
11
13
  - Inline comments stripped automatically (`# ...` and `// ...`)
12
14
  - `--dry-run` parses and validates without touching the file system
13
15
  - `--verbose` prints every created path
@@ -20,10 +22,10 @@ Pass a tree string — drawn with box-drawing characters or plain indentation
20
22
  npm install -g touch-all
21
23
  ```
22
24
 
23
- Or run without installing:
25
+ or with `npx` without installing:
24
26
 
25
27
  ```bash
26
- npx touch-all "..."
28
+ npx touch-all@latest "..."
27
29
  ```
28
30
 
29
31
  ## Usage
@@ -41,37 +43,37 @@ my-project/
41
43
  "
42
44
  ```
43
45
 
44
- ### Specify target directory
46
+ ## Arguments
47
+
48
+ - `--path` , `-p` – specifies target directory. By default, the current working directory is used. Can be an absolute path or a path relative to the current working directory.
45
49
 
46
50
  ```bash
47
- touch-all "..." --path ./my-project
48
- touch-all "..." -p ./my-project
51
+ touch-all "..." --path=./my-project
52
+ touch-all "..." -p ~/Documents/my-project
49
53
  ```
50
54
 
51
- ### Dry run parse and validate, no files created
55
+ - `--dry-run` , `-n` – parses and validates the tree string without creating any files or directories. Useful for testing and debugging.
52
56
 
53
57
  ```bash
54
58
  touch-all "..." --dry-run
55
59
  touch-all "..." -n
56
60
  ```
57
61
 
58
- ### Verbose output
62
+ - `--verbose` , `-v` – prints every created path to the console. Useful for seeing exactly what will be created, especially with complex structures. It's an alias for `--log-level info`
59
63
 
60
64
  ```bash
61
65
  touch-all "..." --verbose
62
66
  touch-all "..." -v
63
67
  ```
64
68
 
65
- ### Help
66
-
67
- ```bash
68
- touch-all --help
69
- ```
69
+ - `--completions` – generates a completion script for a specific shell. Supported shells: `sh`, `bash`, `fish`, `zsh`.
70
+ - `--log-level` – sets the minimum log level for a command. Supported levels: `all`, `trace`, `debug`, `info`, `warning`, `error`, `fatal`, `none`. The default log level is `warning`.
71
+ - `--help` , `-h` – shows the help documentation for a command.
72
+ - `--wizard` – starts wizard mode for a command, providing an interactive step-by-step interface.
73
+ - `--version` – shows the version of the application.
70
74
 
71
75
  ## Tree Format
72
76
 
73
- Both formats produce identical results.
74
-
75
77
  ### Box-drawing characters
76
78
 
77
79
  ```
@@ -100,6 +102,8 @@ my-project/
100
102
  README.md
101
103
  ```
102
104
 
105
+ Both formats produce identical results.
106
+
103
107
  ### Rules
104
108
 
105
109
  | Syntax | Meaning |
@@ -108,8 +112,8 @@ my-project/
108
112
  | `name` | file |
109
113
  | `dir/sub/` | directory at an explicit subpath |
110
114
  | `dir/sub/file.ts` | file at an explicit subpath |
111
- | `# comment` | ignored (stripped) |
112
- | `// comment` | ignored (stripped) |
115
+ | `... # comment` | ignored (stripped) |
116
+ | `... // comment` | ignored (stripped) |
113
117
 
114
118
  Items at the root level (no indentation / no parent) are created directly inside the target directory.
115
119
 
@@ -126,21 +130,28 @@ import {
126
130
  resolveProjectPathToBase,
127
131
  PathTraversalError,
128
132
  FsError,
129
- cli,
130
133
  } from 'touch-all'
131
134
  import type { ParserResult, ParserResultLineItem } from 'touch-all'
132
135
  ```
133
136
 
134
137
  ### `parserFolderStructure(tree: string): ParserResult`
135
138
 
136
- Parses a tree string into a flat list of `{ path, isFile }` items. Pure function, no I/O.
139
+ Parses a tree string into a flat list of `type ParserResult = { path: string, isFile: boolean }` items. Pure function, no I/O.
137
140
 
138
141
  ```ts
139
142
  const items = parserFolderStructure(`
140
143
  src/
141
144
  index.ts
142
145
  `)
143
- // [{ path: 'src', isFile: false }, { path: 'src/index.ts', isFile: true }]
146
+ // [
147
+ // {
148
+ // path: 'src',
149
+ // isFile: false
150
+ // }, {
151
+ // path: 'src/index.ts',
152
+ // isFile: true
153
+ // }
154
+ // ]
144
155
  ```
145
156
 
146
157
  ### `fileStructureCreator(items: ParserResult, basePath: string): Effect<void, FsError | PathTraversalError>`
@@ -151,15 +162,19 @@ Creates the parsed structure on disk under `basePath`. Returns an [Effect](https
151
162
  import { Effect } from 'effect'
152
163
  import { NodeContext, NodeRuntime } from '@effect/platform-node'
153
164
 
165
+ const projectDirectory = '/absolute/target/path'
154
166
  const items = parserFolderStructure(tree)
155
167
 
156
- fileStructureCreator(items, '/absolute/target/path').pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain)
168
+ fileStructureCreator(items, projectDirectory).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain)
157
169
  ```
158
170
 
159
171
  ### `resolveProjectPathToBase(projectPath: string, basePath: string): Effect<string, PathTraversalError>`
160
172
 
161
173
  Resolves `projectPath` relative to `basePath` and rejects any path that would escape `basePath` (path traversal protection).
162
174
 
175
+ > [!CAUTION]
176
+ > `projectPath` cannot traverse outside of `basePath`. If `projectPath` is absolute, it treated as relative to `basePath`. If `projectPath` is relative, it is resolved against `basePath`. In either case, if the resulting path is outside of `basePath`, a `PathTraversalError` is thrown.
177
+
163
178
  ### Error types
164
179
 
165
180
  | Class | `_tag` | When thrown |
package/dist/lib/index.js CHANGED
@@ -42,31 +42,31 @@ var resolveProjectPathToBase = (projectPath, basePath) => {
42
42
  };
43
43
 
44
44
  // src/fsGenerator.ts
45
- var fileStructureCreator = (items, basePath) => Effect2.gen(function* (_) {
46
- yield* _(Effect2.logInfo(`Creating structure in: ${basePath}`));
45
+ var fileStructureCreator = (items, basePath) => Effect2.gen(function* () {
46
+ yield* Effect2.logInfo(`Creating structure in: ${basePath}`);
47
47
  for (const item of items) {
48
- const fullPath = yield* _(resolveProjectPathToBase(item.path, basePath));
48
+ const fullPath = yield* resolveProjectPathToBase(item.path, basePath);
49
49
  if (item.isFile) {
50
50
  const dir = path.dirname(fullPath);
51
- yield* _(Effect2.try({
51
+ yield* Effect2.try({
52
52
  try: () => fs.mkdirSync(dir, { recursive: true }),
53
53
  catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
54
- }));
55
- yield* _(Effect2.try({
54
+ });
55
+ yield* Effect2.try({
56
56
  try: () => fs.writeFileSync(fullPath, ""),
57
57
  catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
58
- }));
59
- yield* _(Effect2.logInfo(` Created file: ${item.path}`));
58
+ });
59
+ yield* Effect2.logInfo(` Created file: ${item.path}`);
60
60
  } else {
61
- yield* _(Effect2.try({
61
+ yield* Effect2.try({
62
62
  try: () => fs.mkdirSync(fullPath, { recursive: true }),
63
63
  catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
64
- }));
65
- yield* _(Effect2.logInfo(` Created directory: ${item.path}`));
64
+ });
65
+ yield* Effect2.logInfo(` Created directory: ${item.path}`);
66
66
  }
67
67
  }
68
- yield* _(Effect2.logInfo(`
69
- ✓ Structure created successfully!`));
68
+ yield* Effect2.logInfo(`
69
+ ✓ Structure created successfully!`);
70
70
  });
71
71
  // src/parser.ts
72
72
  var parserFolderStructure = (treeString) => {
@@ -80,13 +80,11 @@ var parserFolderStructure = (treeString) => {
80
80
  const [p01 = "", _comment01] = line.split("#");
81
81
  const [p02 = "", _comment02] = p01.split("//");
82
82
  const p03 = p02.replace(/[│├└─\t]/g, " ");
83
- const cleanLine = p03.trim();
83
+ const cleanLine = p03.trim().replace(/^\.\//, "");
84
84
  if (!cleanLine)
85
85
  continue;
86
86
  if (cleanLine === "/")
87
87
  continue;
88
- if (cleanLine.startsWith("./"))
89
- continue;
90
88
  if (cleanLine.endsWith("../"))
91
89
  continue;
92
90
  const indent = countLeadingSpaces(p03);
@@ -114,62 +112,10 @@ function countLeadingSpaces(str) {
114
112
  const match = str.match(/^[\s]*/);
115
113
  return match ? match[0].length : 0;
116
114
  }
117
- // src/cli.ts
118
- import { Args, Command, Options } from "@effect/cli";
119
- import { Console, Effect as Effect3, Logger, LogLevel, Option } from "effect";
120
- import { createInterface } from "node:readline";
121
- var treeArg = Args.text({ name: "tree" }).pipe(Args.withDescription("Multiline string representing the directory tree structure"), Args.optional);
122
- var pathOption = Options.directory("path").pipe(Options.withAlias("p"), Options.withDefault("."), Options.withDescription("Target folder path (defaults to current directory)"));
123
- var dryRunOption = Options.boolean("dry-run").pipe(Options.withAlias("n"), Options.withDefault(false), Options.withDescription("Skip the top-level directory if there is only one"));
124
- var verboseOption = Options.boolean("verbose").pipe(Options.withAlias("v"), Options.withDefault(false), Options.withDescription("Log to console extra information about creating a directory tree"));
125
- var command = Command.make("touch-all", {
126
- tree: treeArg,
127
- path: pathOption,
128
- dryRun: dryRunOption,
129
- verbose: verboseOption
130
- }).pipe(Command.withDescription("Create directory structure from a tree representation"), Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
131
- const readStdin = Effect3.tryPromise({
132
- try: () => new Promise((resolve2) => {
133
- const rl = createInterface({ input: process.stdin });
134
- const lines = [];
135
- rl.on("line", (line) => lines.push(line));
136
- rl.on("close", () => resolve2(lines.join(`
137
- `)));
138
- }),
139
- catch: (e) => new Error(`Failed to read stdin: ${String(e)}`)
140
- });
141
- const program = Effect3.gen(function* (_) {
142
- const treeString = Option.isSome(tree) ? tree.value : yield* _(readStdin);
143
- if (dryRun) {
144
- yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
145
- }
146
- yield* _(Effect3.logInfo("Parsing tree structure..."));
147
- const items = parserFolderStructure(treeString);
148
- if (items.length === 0) {
149
- yield* _(Console.error("No valid items found in the tree structure"));
150
- yield* _(Console.error(items));
151
- yield* _(Console.error(treeString));
152
- return yield* _(Effect3.fail(new Error("Invalid tree structure")));
153
- }
154
- yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
155
- yield* _(Effect3.logInfo(`Found:
156
- ${items.map((i) => `${i.path}
157
- `).join("")}`));
158
- if (!dryRun) {
159
- yield* _(fileStructureCreator(items, targetPath));
160
- }
161
- });
162
- return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
163
- }));
164
- var cli = Command.run(command, {
165
- name: "Touch All",
166
- version: "0.0.1"
167
- });
168
115
  export {
169
116
  resolveProjectPathToBase,
170
117
  parserFolderStructure,
171
118
  fileStructureCreator,
172
- cli,
173
119
  PathTraversalError,
174
120
  FsError
175
121
  };
@@ -3,4 +3,3 @@ export { resolveProjectPathToBase } from './fsNormalizator';
3
3
  export { PathTraversalError, FsError } from './_commonErrors';
4
4
  export type { ParserResult, ParserResultLineItem } from './_commonTypes';
5
5
  export { parserFolderStructure } from './parser';
6
- export { cli } from './cli';
@@ -4,9 +4,9 @@ import { Effect as Effect4, Logger as Logger2, LogLevel as LogLevel2 } from "eff
4
4
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
5
5
 
6
6
  // src/cli.ts
7
+ import { createInterface } from "node:readline";
7
8
  import { Args, Command, Options } from "@effect/cli";
8
9
  import { Console, Effect as Effect3, Logger, LogLevel, Option } from "effect";
9
- import { createInterface } from "node:readline";
10
10
 
11
11
  // src/parser.ts
12
12
  var parserFolderStructure = (treeString) => {
@@ -20,13 +20,11 @@ var parserFolderStructure = (treeString) => {
20
20
  const [p01 = "", _comment01] = line.split("#");
21
21
  const [p02 = "", _comment02] = p01.split("//");
22
22
  const p03 = p02.replace(/[│├└─\t]/g, " ");
23
- const cleanLine = p03.trim();
23
+ const cleanLine = p03.trim().replace(/^\.\//, "");
24
24
  if (!cleanLine)
25
25
  continue;
26
26
  if (cleanLine === "/")
27
27
  continue;
28
- if (cleanLine.startsWith("./"))
29
- continue;
30
28
  if (cleanLine.endsWith("../"))
31
29
  continue;
32
30
  const indent = countLeadingSpaces(p03);
@@ -99,37 +97,103 @@ var resolveProjectPathToBase = (projectPath, basePath) => {
99
97
  };
100
98
 
101
99
  // src/fsGenerator.ts
102
- var fileStructureCreator = (items, basePath) => Effect2.gen(function* (_) {
103
- yield* _(Effect2.logInfo(`Creating structure in: ${basePath}`));
100
+ var fileStructureCreator = (items, basePath) => Effect2.gen(function* () {
101
+ yield* Effect2.logInfo(`Creating structure in: ${basePath}`);
104
102
  for (const item of items) {
105
- const fullPath = yield* _(resolveProjectPathToBase(item.path, basePath));
103
+ const fullPath = yield* resolveProjectPathToBase(item.path, basePath);
106
104
  if (item.isFile) {
107
105
  const dir = path.dirname(fullPath);
108
- yield* _(Effect2.try({
106
+ yield* Effect2.try({
109
107
  try: () => fs.mkdirSync(dir, { recursive: true }),
110
108
  catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
111
- }));
112
- yield* _(Effect2.try({
109
+ });
110
+ yield* Effect2.try({
113
111
  try: () => fs.writeFileSync(fullPath, ""),
114
112
  catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
115
- }));
116
- yield* _(Effect2.logInfo(` Created file: ${item.path}`));
113
+ });
114
+ yield* Effect2.logInfo(` Created file: ${item.path}`);
117
115
  } else {
118
- yield* _(Effect2.try({
116
+ yield* Effect2.try({
119
117
  try: () => fs.mkdirSync(fullPath, { recursive: true }),
120
118
  catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
121
- }));
122
- yield* _(Effect2.logInfo(` Created directory: ${item.path}`));
119
+ });
120
+ yield* Effect2.logInfo(` Created directory: ${item.path}`);
123
121
  }
124
122
  }
125
- yield* _(Effect2.logInfo(`
126
- ✓ Structure created successfully!`));
123
+ yield* Effect2.logInfo(`
124
+ ✓ Structure created successfully!`);
127
125
  });
126
+ // package.json
127
+ var package_default = {
128
+ name: "touch-all",
129
+ version: "1.2.1",
130
+ description: "CLI tool to create folder structures from markdown tree representations",
131
+ keywords: [
132
+ "cli",
133
+ "folders",
134
+ "generator",
135
+ "markdown",
136
+ "structure"
137
+ ],
138
+ license: "GPL-3.0-or-later",
139
+ author: "Anton Huz <anton@ahuz.dev>",
140
+ repository: {
141
+ type: "git",
142
+ url: "https://github.com/anton-huz/touch-all"
143
+ },
144
+ bin: {
145
+ "touch-all": "dist/slim/touch-all.js"
146
+ },
147
+ files: [
148
+ "dist/slim",
149
+ "dist/lib"
150
+ ],
151
+ type: "module",
152
+ main: "dist/lib/index.js",
153
+ types: "dist/lib/index.d.ts",
154
+ exports: {
155
+ ".": {
156
+ types: "./dist/lib/index.d.ts",
157
+ import: "./dist/lib/index.js"
158
+ }
159
+ },
160
+ publishConfig: {
161
+ access: "public",
162
+ provenance: true
163
+ },
164
+ scripts: {
165
+ build: "tsup --config .config/tsup.config.ts",
166
+ "check:lint": "oxlint . -c .config/.oxlintrc.json",
167
+ "check:format": "oxfmt . -c .config/.oxfmtrc.json --check",
168
+ "check:ts": "tsc --noEmit",
169
+ format: "oxfmt . -c .config/.oxfmtrc.json --write",
170
+ test: "vitest --config .config/vitest.config.ts run"
171
+ },
172
+ dependencies: {
173
+ "@effect/cli": "^0.75.0",
174
+ "@effect/platform-node": "^0.106.0",
175
+ effect: "^3.21.0"
176
+ },
177
+ devDependencies: {
178
+ "@total-typescript/tsconfig": "1.0.3",
179
+ "@types/bun": "1.3.11",
180
+ "@types/node": "25.5.0",
181
+ oxfmt: "0.41.0",
182
+ oxlint: "1.56.0",
183
+ tsup: "8.5.1",
184
+ typescript: "5.9.3",
185
+ vitest: "4.1.0"
186
+ },
187
+ engines: {
188
+ node: ">=18"
189
+ },
190
+ url: "https://github.com/anton-huz/touch-all"
191
+ };
128
192
 
129
193
  // src/cli.ts
130
194
  var treeArg = Args.text({ name: "tree" }).pipe(Args.withDescription("Multiline string representing the directory tree structure"), Args.optional);
131
195
  var pathOption = Options.directory("path").pipe(Options.withAlias("p"), Options.withDefault("."), Options.withDescription("Target folder path (defaults to current directory)"));
132
- var dryRunOption = Options.boolean("dry-run").pipe(Options.withAlias("n"), Options.withDefault(false), Options.withDescription("Skip the top-level directory if there is only one"));
196
+ var dryRunOption = Options.boolean("dry-run").pipe(Options.withAlias("n"), Options.withDefault(false), Options.withDescription("Parse and validate the tree without writing to the file system"));
133
197
  var verboseOption = Options.boolean("verbose").pipe(Options.withAlias("v"), Options.withDefault(false), Options.withDescription("Log to console extra information about creating a directory tree"));
134
198
  var command = Command.make("touch-all", {
135
199
  tree: treeArg,
@@ -137,42 +201,45 @@ var command = Command.make("touch-all", {
137
201
  dryRun: dryRunOption,
138
202
  verbose: verboseOption
139
203
  }).pipe(Command.withDescription("Create directory structure from a tree representation"), Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
140
- const readStdin = Effect3.tryPromise({
141
- try: () => new Promise((resolve2) => {
142
- const rl = createInterface({ input: process.stdin });
143
- const lines = [];
144
- rl.on("line", (line) => lines.push(line));
145
- rl.on("close", () => resolve2(lines.join(`
204
+ const readStdin = Effect3.gen(function* () {
205
+ if (process.stdin.isTTY) {
206
+ yield* Console.log("Paste your tree structure and press Ctrl+D when done:");
207
+ }
208
+ return yield* Effect3.tryPromise({
209
+ try: () => new Promise((resolve2) => {
210
+ const rl = createInterface({ input: process.stdin });
211
+ const lines = [];
212
+ rl.on("line", (line) => lines.push(line));
213
+ rl.on("close", () => resolve2(lines.join(`
146
214
  `)));
147
- }),
148
- catch: (e) => new Error(`Failed to read stdin: ${String(e)}`)
215
+ }),
216
+ catch: (e) => new Error(`Failed to read stdin: ${String(e)}`)
217
+ });
149
218
  });
150
- const program = Effect3.gen(function* (_) {
151
- const treeString = Option.isSome(tree) ? tree.value : yield* _(readStdin);
219
+ const program = Effect3.gen(function* () {
220
+ const treeString = Option.isSome(tree) ? tree.value : yield* readStdin;
152
221
  if (dryRun) {
153
- yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
222
+ yield* Effect3.logInfo("Running in dry mode. No one file system node will be created.");
154
223
  }
155
- yield* _(Effect3.logInfo("Parsing tree structure..."));
224
+ yield* Effect3.logInfo("Parsing tree structure...");
156
225
  const items = parserFolderStructure(treeString);
157
226
  if (items.length === 0) {
158
- yield* _(Console.error("No valid items found in the tree structure"));
159
- yield* _(Console.error(items));
160
- yield* _(Console.error(treeString));
161
- return yield* _(Effect3.fail(new Error("Invalid tree structure")));
227
+ yield* Console.error("No valid items found in the tree structure");
228
+ return yield* Effect3.fail(new Error("Invalid tree structure"));
162
229
  }
163
- yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
164
- yield* _(Effect3.logInfo(`Found:
230
+ yield* Effect3.logInfo(`Found ${items.length} items to create`);
231
+ yield* Effect3.logInfo(`Found:
165
232
  ${items.map((i) => `${i.path}
166
- `).join("")}`));
233
+ `).join("")}`);
167
234
  if (!dryRun) {
168
- yield* _(fileStructureCreator(items, targetPath));
235
+ yield* fileStructureCreator(items, targetPath);
169
236
  }
170
237
  });
171
238
  return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
172
239
  }));
173
240
  var cli = Command.run(command, {
174
- name: "Touch All",
175
- version: "0.0.1"
241
+ name: package_default.name,
242
+ version: package_default.version
176
243
  });
177
244
 
178
245
  // src/touch-all.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "touch-all",
3
- "version": "1.1.10",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool to create folder structures from markdown tree representations",
5
5
  "keywords": [
6
6
  "cli",
@@ -19,7 +19,7 @@
19
19
  "touch-all": "dist/slim/touch-all.js"
20
20
  },
21
21
  "files": [
22
- "dist/bundled",
22
+ "dist/slim",
23
23
  "dist/lib"
24
24
  ],
25
25
  "type": "module",
@@ -39,23 +39,24 @@
39
39
  "build": "tsup --config .config/tsup.config.ts",
40
40
  "check:lint": "oxlint . -c .config/.oxlintrc.json",
41
41
  "check:format": "oxfmt . -c .config/.oxfmtrc.json --check",
42
+ "check:ts": "tsc --noEmit",
42
43
  "format": "oxfmt . -c .config/.oxfmtrc.json --write",
43
44
  "test": "vitest --config .config/vitest.config.ts run"
44
45
  },
45
46
  "dependencies": {
46
- "@effect/cli": "^0.73.2",
47
- "@effect/platform-node": "^0.104.1",
48
- "effect": "^3.19.16"
47
+ "@effect/cli": "^0.75.0",
48
+ "@effect/platform-node": "^0.106.0",
49
+ "effect": "^3.21.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@total-typescript/tsconfig": "1.0.4",
52
- "@types/bun": "1.3.9",
53
- "@types/node": "25.3.2",
54
- "oxfmt": "0.28.0",
55
- "oxlint": "1.43.0",
52
+ "@total-typescript/tsconfig": "1.0.3",
53
+ "@types/bun": "1.3.11",
54
+ "@types/node": "25.5.0",
55
+ "oxfmt": "0.41.0",
56
+ "oxlint": "1.56.0",
56
57
  "tsup": "8.5.1",
57
58
  "typescript": "5.9.3",
58
- "vitest": "4.0.18"
59
+ "vitest": "4.1.0"
59
60
  },
60
61
  "engines": {
61
62
  "node": ">=18"