@outfitter/file-ops 0.2.3 → 0.2.4

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 (3) hide show
  1. package/README.md +48 -48
  2. package/dist/index.js +4 -3
  3. package/package.json +26 -26
package/README.md CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  securePath,
17
17
  glob,
18
18
  withLock,
19
- atomicWrite
19
+ atomicWrite,
20
20
  } from "@outfitter/file-ops";
21
21
 
22
22
  // Find workspace root by marker files (.git, package.json)
@@ -34,7 +34,7 @@ if (rootResult.isOk()) {
34
34
  // Find files with glob patterns
35
35
  const files = await glob("**/*.ts", {
36
36
  cwd: "/project",
37
- ignore: ["node_modules/**", "**/*.test.ts"]
37
+ ignore: ["node_modules/**", "**/*.test.ts"],
38
38
  });
39
39
 
40
40
  // Atomic write with file locking
@@ -60,16 +60,16 @@ if (result.isOk()) {
60
60
 
61
61
  **Options:**
62
62
 
63
- | Option | Type | Default | Description |
64
- |--------|------|---------|-------------|
63
+ | Option | Type | Default | Description |
64
+ | --------- | ---------- | -------------------------- | -------------------------------------- |
65
65
  | `markers` | `string[]` | `[".git", "package.json"]` | Marker files/directories to search for |
66
- | `stopAt` | `string` | filesystem root | Stop searching at this directory |
66
+ | `stopAt` | `string` | filesystem root | Stop searching at this directory |
67
67
 
68
68
  ```typescript
69
69
  // Custom markers for Rust or Python projects
70
70
  const result = await findWorkspaceRoot(startPath, {
71
71
  markers: ["Cargo.toml", "pyproject.toml"],
72
- stopAt: "/home/user"
72
+ stopAt: "/home/user",
73
73
  });
74
74
  ```
75
75
 
@@ -102,12 +102,12 @@ console.log(outside); // false
102
102
 
103
103
  #### Security Model
104
104
 
105
- | Attack Vector | Protection |
106
- |--------------|------------|
107
- | Path traversal (`../`) | Blocked by all security functions |
108
- | Null bytes (`\x00`) | Rejected immediately |
109
- | Absolute paths | Blocked when relative expected |
110
- | Escape from base directory | Defense-in-depth verification |
105
+ | Attack Vector | Protection |
106
+ | -------------------------- | --------------------------------- |
107
+ | Path traversal (`../`) | Blocked by all security functions |
108
+ | Null bytes (`\x00`) | Rejected immediately |
109
+ | Absolute paths | Blocked when relative expected |
110
+ | Escape from base directory | Defense-in-depth verification |
111
111
 
112
112
  #### `securePath(path, basePath)`
113
113
 
@@ -122,9 +122,9 @@ if (result.isOk()) {
122
122
  }
123
123
 
124
124
  // These all return ValidationError:
125
- securePath("../etc/passwd", base); // Traversal sequence
126
- securePath("/etc/passwd", base); // Absolute path
127
- securePath("file\x00.txt", base); // Null byte
125
+ securePath("../etc/passwd", base); // Traversal sequence
126
+ securePath("/etc/passwd", base); // Absolute path
127
+ securePath("file\x00.txt", base); // Null byte
128
128
  ```
129
129
 
130
130
  **UNSAFE pattern - never do this:**
@@ -161,8 +161,8 @@ if (result.isOk()) {
161
161
  }
162
162
 
163
163
  // Rejects dangerous segments
164
- resolveSafePath("/app", "..", "etc"); // Error: traversal
165
- resolveSafePath("/app", "/etc/passwd"); // Error: absolute segment
164
+ resolveSafePath("/app", "..", "etc"); // Error: traversal
165
+ resolveSafePath("/app", "/etc/passwd"); // Error: absolute segment
166
166
  ```
167
167
 
168
168
  ### Glob Patterns
@@ -178,7 +178,7 @@ const result = await glob("**/*.ts", { cwd: "/project" });
178
178
  // Exclude test files and node_modules
179
179
  const result = await glob("**/*.ts", {
180
180
  cwd: "/project",
181
- ignore: ["**/*.test.ts", "**/node_modules/**"]
181
+ ignore: ["**/*.test.ts", "**/node_modules/**"],
182
182
  });
183
183
 
184
184
  // Include dot files
@@ -187,28 +187,28 @@ const result = await glob("**/.*", { cwd: "/project", dot: true });
187
187
 
188
188
  **Options:**
189
189
 
190
- | Option | Type | Default | Description |
191
- |--------|------|---------|-------------|
192
- | `cwd` | `string` | `process.cwd()` | Base directory for matching |
193
- | `ignore` | `string[]` | `[]` | Patterns to exclude |
194
- | `followSymlinks` | `boolean` | `false` | Follow symbolic links |
195
- | `dot` | `boolean` | `false` | Include dot files |
190
+ | Option | Type | Default | Description |
191
+ | ---------------- | ---------- | --------------- | --------------------------- |
192
+ | `cwd` | `string` | `process.cwd()` | Base directory for matching |
193
+ | `ignore` | `string[]` | `[]` | Patterns to exclude |
194
+ | `followSymlinks` | `boolean` | `false` | Follow symbolic links |
195
+ | `dot` | `boolean` | `false` | Include dot files |
196
196
 
197
197
  **Pattern Syntax:**
198
198
 
199
- | Pattern | Matches |
200
- |---------|---------|
201
- | `*` | Any characters except `/` |
202
- | `**` | Any characters including `/` (recursive) |
203
- | `{a,b}` | Alternation (matches `a` or `b`) |
204
- | `[abc]` | Character class (matches `a`, `b`, or `c`) |
205
- | `!pattern` | Negation (in ignore array) |
199
+ | Pattern | Matches |
200
+ | ---------- | ------------------------------------------ |
201
+ | `*` | Any characters except `/` |
202
+ | `**` | Any characters including `/` (recursive) |
203
+ | `{a,b}` | Alternation (matches `a` or `b`) |
204
+ | `[abc]` | Character class (matches `a`, `b`, or `c`) |
205
+ | `!pattern` | Negation (in ignore array) |
206
206
 
207
207
  ```typescript
208
208
  // Negation patterns in ignore array
209
209
  const result = await glob("src/**/*.ts", {
210
210
  cwd: "/project",
211
- ignore: ["**/*.ts", "!**/index.ts"] // Ignore all except index.ts
211
+ ignore: ["**/*.ts", "!**/index.ts"], // Ignore all except index.ts
212
212
  });
213
213
  ```
214
214
 
@@ -275,9 +275,9 @@ if (await isLocked("/data/file.db")) {
275
275
 
276
276
  ```typescript
277
277
  interface FileLock {
278
- path: string; // Path to the locked file
279
- lockPath: string; // Path to the .lock file
280
- pid: number; // Process ID holding the lock
278
+ path: string; // Path to the locked file
279
+ lockPath: string; // Path to the .lock file
280
+ pid: number; // Process ID holding the lock
281
281
  timestamp: number; // When lock was acquired
282
282
  }
283
283
  ```
@@ -299,21 +299,21 @@ if (result.isErr()) {
299
299
 
300
300
  **Options:**
301
301
 
302
- | Option | Type | Default | Description |
303
- |--------|------|---------|-------------|
304
- | `createParentDirs` | `boolean` | `true` | Create parent directories if needed |
302
+ | Option | Type | Default | Description |
303
+ | --------------------- | --------- | ------- | ----------------------------------- |
304
+ | `createParentDirs` | `boolean` | `true` | Create parent directories if needed |
305
305
  | `preservePermissions` | `boolean` | `false` | Keep permissions from existing file |
306
- | `mode` | `number` | `0o644` | File mode for new files |
306
+ | `mode` | `number` | `0o644` | File mode for new files |
307
307
 
308
308
  ```typescript
309
309
  // Preserve executable permissions
310
310
  await atomicWrite("/scripts/run.sh", newContent, {
311
- preservePermissions: true
311
+ preservePermissions: true,
312
312
  });
313
313
 
314
314
  // Create nested directories automatically
315
315
  await atomicWrite("/data/deep/nested/file.json", content, {
316
- createParentDirs: true
316
+ createParentDirs: true,
317
317
  });
318
318
  ```
319
319
 
@@ -325,7 +325,7 @@ Serializes and writes JSON data atomically.
325
325
  const result = await atomicWriteJson("/data/config.json", {
326
326
  name: "app",
327
327
  version: "1.0.0",
328
- settings: { debug: false }
328
+ settings: { debug: false },
329
329
  });
330
330
  ```
331
331
 
@@ -348,12 +348,12 @@ if (result.isOk()) {
348
348
 
349
349
  **Error Types:**
350
350
 
351
- | Error | Functions | When |
352
- |-------|-----------|------|
353
- | `NotFoundError` | `findWorkspaceRoot`, `getRelativePath` | No workspace marker found |
354
- | `ValidationError` | `securePath`, `isPathSafe`, `resolveSafePath`, `atomicWriteJson` | Invalid path or data |
355
- | `ConflictError` | `acquireLock`, `withLock` | File already locked |
356
- | `InternalError` | `glob`, `releaseLock`, `withLock`, `atomicWrite` | Filesystem or system error |
351
+ | Error | Functions | When |
352
+ | ----------------- | ---------------------------------------------------------------- | -------------------------- |
353
+ | `NotFoundError` | `findWorkspaceRoot`, `getRelativePath` | No workspace marker found |
354
+ | `ValidationError` | `securePath`, `isPathSafe`, `resolveSafePath`, `atomicWriteJson` | Invalid path or data |
355
+ | `ConflictError` | `acquireLock`, `withLock` | File already locked |
356
+ | `InternalError` | `glob`, `releaseLock`, `withLock`, `atomicWrite` | Filesystem or system error |
357
357
 
358
358
  ## Dependencies
359
359
 
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
- // src/index.ts
1
+ // @bun
2
+ // packages/file-ops/src/index.ts
2
3
  import {
3
4
  writeFile as fsWriteFile,
4
5
  mkdir,
5
6
  rename,
6
7
  stat,
7
8
  unlink
8
- } from "node:fs/promises";
9
+ } from "fs/promises";
9
10
  import {
10
11
  dirname,
11
12
  isAbsolute,
@@ -14,7 +15,7 @@ import {
14
15
  relative,
15
16
  resolve,
16
17
  sep
17
- } from "node:path";
18
+ } from "path";
18
19
  import {
19
20
  ConflictError,
20
21
  InternalError,
package/package.json CHANGED
@@ -1,11 +1,25 @@
1
1
  {
2
2
  "name": "@outfitter/file-ops",
3
+ "version": "0.2.4",
3
4
  "description": "Workspace detection, secure path handling, and file locking for Outfitter",
4
- "version": "0.2.3",
5
- "type": "module",
5
+ "keywords": [
6
+ "file-ops",
7
+ "outfitter",
8
+ "security",
9
+ "typescript",
10
+ "workspace"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/outfitter-dev/outfitter.git",
16
+ "directory": "packages/file-ops"
17
+ },
6
18
  "files": [
7
19
  "dist"
8
20
  ],
21
+ "type": "module",
22
+ "sideEffects": false,
9
23
  "module": "./dist/index.js",
10
24
  "types": "./dist/index.d.ts",
11
25
  "exports": {
@@ -17,38 +31,24 @@
17
31
  },
18
32
  "./package.json": "./package.json"
19
33
  },
20
- "sideEffects": false,
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
21
37
  "scripts": {
22
- "build": "bunup --filter @outfitter/file-ops",
23
- "lint": "biome lint ./src",
24
- "lint:fix": "biome lint --write ./src",
38
+ "build": "cd ../.. && bunup --filter @outfitter/file-ops",
39
+ "lint": "oxlint ./src",
40
+ "lint:fix": "oxlint --fix ./src",
25
41
  "test": "bun test",
26
42
  "typecheck": "tsc --noEmit",
27
43
  "clean": "rm -rf dist",
28
44
  "prepublishOnly": "bun ../../scripts/check-publish-manifest.ts"
29
45
  },
30
46
  "dependencies": {
31
- "@outfitter/contracts": "0.4.1",
32
- "@outfitter/types": "0.2.3"
47
+ "@outfitter/contracts": "0.4.2",
48
+ "@outfitter/types": "0.2.4"
33
49
  },
34
50
  "devDependencies": {
35
- "@types/bun": "latest",
36
- "typescript": "^5.8.0"
37
- },
38
- "keywords": [
39
- "outfitter",
40
- "file-ops",
41
- "workspace",
42
- "security",
43
- "typescript"
44
- ],
45
- "license": "MIT",
46
- "repository": {
47
- "type": "git",
48
- "url": "https://github.com/outfitter-dev/outfitter.git",
49
- "directory": "packages/file-ops"
50
- },
51
- "publishConfig": {
52
- "access": "public"
51
+ "@types/bun": "^1.3.9",
52
+ "typescript": "^5.9.3"
53
53
  }
54
54
  }