@effected/walker 0.3.2 → 0.3.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/Walker.js +58 -65
  2. package/index.d.ts +59 -6
  3. package/package.json +2 -2
package/Walker.js CHANGED
@@ -1,30 +1,6 @@
1
1
  import { Effect, FileSystem, Option, Path } from "effect";
2
2
 
3
3
  //#region src/Walker.ts
4
- /**
5
- * Ascend from `start` toward the filesystem root, yielding each directory,
6
- * nearest first.
7
- *
8
- * @remarks
9
- * Lexical, not physical: `Path.dirname` does not resolve symlinks, so ascending
10
- * out of a symlinked directory follows the path you were given rather than the
11
- * real filesystem parent. That is deliberate — config discovery wants the file
12
- * nearest the path the user named.
13
- *
14
- * Bounded twice over: `dirname` is a fixpoint at the root, and `maxDepth` guards
15
- * a pathological `Path` implementation that never reaches one.
16
- *
17
- * `stopAt` is compared in normalized form — see {@link AscendOptions.stopAt}.
18
- * Raw string equality made the ceiling fail OPEN: an unnormalized ceiling
19
- * matched nothing and the ascent ran silently to the filesystem root, which is
20
- * the unbounded walk the option exists to prevent, with no error to notice it
21
- * by. A relative ceiling **dies** for the same reason — resolving one against
22
- * `process.cwd()` would reintroduce the silent-wrong-walk failure through a
23
- * different door. See {@link AscendOptions.stopAt} for why that is a defect
24
- * rather than a typed error; the error channel stays `never`.
25
- *
26
- * @public
27
- */
28
4
  const ascend = (start, options) => Effect.gen(function* () {
29
5
  const path = yield* Path.Path;
30
6
  const maxDepth = options?.maxDepth ?? 256;
@@ -42,63 +18,80 @@ const ascend = (start, options) => Effect.gen(function* () {
42
18
  }
43
19
  return dirs;
44
20
  });
45
- /**
46
- * The first candidate whose `predicate` reports true, or `Option.none()`.
47
- *
48
- * @remarks
49
- * Each predicate is absorbed **individually**: a failure on one candidate is
50
- * treated as "this candidate did not match" and the scan continues. One
51
- * unreadable ancestor must never abort the walk, or a permission error deep in
52
- * the tree would hide a valid root above it. Not-found and cannot-look are
53
- * therefore indistinguishable to the caller; discovery is best-effort.
54
- *
55
- * `Effect.catch` catches failures, **not defects**. A predicate that throws is
56
- * programmer error and surfaces as a defect. Do not change this to
57
- * `Effect.catchCause` — the distinction is load-bearing.
58
- *
59
- * @public
60
- */
61
21
  const firstMatch = (candidates, predicate) => Effect.gen(function* () {
62
22
  for (const candidate of candidates) if (yield* Effect.catch(predicate(candidate), () => Effect.succeed(false))) return Option.some(candidate);
63
23
  return Option.none();
64
24
  });
65
- /**
66
- * The first existing path among the candidates `candidatesFor` produces for each
67
- * directory in `dirs`, scanned in order. Nearer directories win.
68
- *
69
- * @remarks
70
- * Candidates materialize up front, bounded by `dirs.length × candidatesFor`'s
71
- * output — a few hundred strings under the default `maxDepth`.
72
- *
73
- * @public
74
- */
75
25
  const findUpward = (dirs, candidatesFor) => Effect.gen(function* () {
76
26
  const fs = yield* FileSystem.FileSystem;
77
27
  const candidates = [];
78
28
  for (const dir of dirs) candidates.push(...candidatesFor(dir));
79
29
  return yield* firstMatch(candidates, (candidate) => fs.exists(candidate));
80
30
  });
81
- /**
82
- * The first directory in `dirs` that `isRoot` accepts.
83
- *
84
- * @remarks
85
- * `firstMatch` over the directories themselves — the candidate expansion is the
86
- * identity. `isRoot` is a caller-supplied marker test (a `.git` entry, a
87
- * `pnpm-workspace.yaml`), and its failures are absorbed per directory.
88
- *
89
- * @public
90
- */
91
31
  const findRoot = (dirs, isRoot) => firstMatch(dirs, isRoot);
92
32
  /**
93
33
  * Upward path traversal primitives.
94
34
  *
95
35
  * @public
96
36
  */
97
- const Walker = {
98
- ascend,
99
- firstMatch,
100
- findUpward,
101
- findRoot
37
+ var Walker = class {
38
+ constructor() {}
39
+ /**
40
+ * Ascend from `start` toward the filesystem root, yielding each directory,
41
+ * nearest first.
42
+ *
43
+ * @remarks
44
+ * Lexical, not physical: `Path.dirname` does not resolve symlinks, so ascending
45
+ * out of a symlinked directory follows the path you were given rather than the
46
+ * real filesystem parent. That is deliberate — config discovery wants the file
47
+ * nearest the path the user named.
48
+ *
49
+ * Bounded twice over: `dirname` is a fixpoint at the root, and `maxDepth` guards
50
+ * a pathological `Path` implementation that never reaches one.
51
+ *
52
+ * `stopAt` is compared in normalized form — see {@link AscendOptions.stopAt}.
53
+ * Raw string equality made the ceiling fail OPEN: an unnormalized ceiling
54
+ * matched nothing and the ascent ran silently to the filesystem root, which is
55
+ * the unbounded walk the option exists to prevent, with no error to notice it
56
+ * by. A relative ceiling **dies** for the same reason — resolving one against
57
+ * `process.cwd()` would reintroduce the silent-wrong-walk failure through a
58
+ * different door. See {@link AscendOptions.stopAt} for why that is a defect
59
+ * rather than a typed error; the error channel stays `never`.
60
+ */
61
+ static ascend = ascend;
62
+ /**
63
+ * The first candidate whose `predicate` reports true, or `Option.none()`.
64
+ *
65
+ * @remarks
66
+ * Each predicate is absorbed **individually**: a failure on one candidate is
67
+ * treated as "this candidate did not match" and the scan continues. One
68
+ * unreadable ancestor must never abort the walk, or a permission error deep in
69
+ * the tree would hide a valid root above it. Not-found and cannot-look are
70
+ * therefore indistinguishable to the caller; discovery is best-effort.
71
+ *
72
+ * `Effect.catch` catches failures, **not defects**. A predicate that throws is
73
+ * programmer error and surfaces as a defect. Do not change this to
74
+ * `Effect.catchCause` — the distinction is load-bearing.
75
+ */
76
+ static firstMatch = firstMatch;
77
+ /**
78
+ * The first existing path among the candidates `candidatesFor` produces for each
79
+ * directory in `dirs`, scanned in order. Nearer directories win.
80
+ *
81
+ * @remarks
82
+ * Candidates materialize up front, bounded by `dirs.length × candidatesFor`'s
83
+ * output — a few hundred strings under the default `maxDepth`.
84
+ */
85
+ static findUpward = findUpward;
86
+ /**
87
+ * The first directory in `dirs` that `isRoot` accepts.
88
+ *
89
+ * @remarks
90
+ * `firstMatch` over the directories themselves — the candidate expansion is the
91
+ * identity. `isRoot` is a caller-supplied marker test (a `.git` entry, a
92
+ * `pnpm-workspace.yaml`), and its failures are absorbed per directory.
93
+ */
94
+ static findRoot = findRoot;
102
95
  };
103
96
 
104
97
  //#endregion
package/index.d.ts CHANGED
@@ -229,12 +229,65 @@ interface AscendOptions {
229
229
  *
230
230
  * @public
231
231
  */
232
- declare const Walker: {
233
- readonly ascend: (start: string, options?: AscendOptions) => Effect.Effect<ReadonlyArray<string>, never, Path.Path>;
234
- readonly firstMatch: <E, R>(candidates: ReadonlyArray<string>, predicate: (candidate: string) => Effect.Effect<boolean, E, R>) => Effect.Effect<Option.Option<string>, never, R>;
235
- readonly findUpward: (dirs: ReadonlyArray<string>, candidatesFor: (dir: string) => ReadonlyArray<string>) => Effect.Effect<Option.Option<string>, never, FileSystem.FileSystem>;
236
- readonly findRoot: <E, R>(dirs: ReadonlyArray<string>, isRoot: (dir: string) => Effect.Effect<boolean, E, R>) => Effect.Effect<Option.Option<string>, never, R>;
237
- };
232
+ declare class Walker {
233
+ private constructor();
234
+ /**
235
+ * Ascend from `start` toward the filesystem root, yielding each directory,
236
+ * nearest first.
237
+ *
238
+ * @remarks
239
+ * Lexical, not physical: `Path.dirname` does not resolve symlinks, so ascending
240
+ * out of a symlinked directory follows the path you were given rather than the
241
+ * real filesystem parent. That is deliberate — config discovery wants the file
242
+ * nearest the path the user named.
243
+ *
244
+ * Bounded twice over: `dirname` is a fixpoint at the root, and `maxDepth` guards
245
+ * a pathological `Path` implementation that never reaches one.
246
+ *
247
+ * `stopAt` is compared in normalized form — see {@link AscendOptions.stopAt}.
248
+ * Raw string equality made the ceiling fail OPEN: an unnormalized ceiling
249
+ * matched nothing and the ascent ran silently to the filesystem root, which is
250
+ * the unbounded walk the option exists to prevent, with no error to notice it
251
+ * by. A relative ceiling **dies** for the same reason — resolving one against
252
+ * `process.cwd()` would reintroduce the silent-wrong-walk failure through a
253
+ * different door. See {@link AscendOptions.stopAt} for why that is a defect
254
+ * rather than a typed error; the error channel stays `never`.
255
+ */
256
+ static readonly ascend: (start: string, options?: AscendOptions) => Effect.Effect<ReadonlyArray<string>, never, Path.Path>;
257
+ /**
258
+ * The first candidate whose `predicate` reports true, or `Option.none()`.
259
+ *
260
+ * @remarks
261
+ * Each predicate is absorbed **individually**: a failure on one candidate is
262
+ * treated as "this candidate did not match" and the scan continues. One
263
+ * unreadable ancestor must never abort the walk, or a permission error deep in
264
+ * the tree would hide a valid root above it. Not-found and cannot-look are
265
+ * therefore indistinguishable to the caller; discovery is best-effort.
266
+ *
267
+ * `Effect.catch` catches failures, **not defects**. A predicate that throws is
268
+ * programmer error and surfaces as a defect. Do not change this to
269
+ * `Effect.catchCause` — the distinction is load-bearing.
270
+ */
271
+ static readonly firstMatch: <E, R>(candidates: ReadonlyArray<string>, predicate: (candidate: string) => Effect.Effect<boolean, E, R>) => Effect.Effect<Option.Option<string>, never, R>;
272
+ /**
273
+ * The first existing path among the candidates `candidatesFor` produces for each
274
+ * directory in `dirs`, scanned in order. Nearer directories win.
275
+ *
276
+ * @remarks
277
+ * Candidates materialize up front, bounded by `dirs.length × candidatesFor`'s
278
+ * output — a few hundred strings under the default `maxDepth`.
279
+ */
280
+ static readonly findUpward: (dirs: ReadonlyArray<string>, candidatesFor: (dir: string) => ReadonlyArray<string>) => Effect.Effect<Option.Option<string>, never, FileSystem.FileSystem>;
281
+ /**
282
+ * The first directory in `dirs` that `isRoot` accepts.
283
+ *
284
+ * @remarks
285
+ * `firstMatch` over the directories themselves — the candidate expansion is the
286
+ * identity. `isRoot` is a caller-supplied marker test (a `.git` entry, a
287
+ * `pnpm-workspace.yaml`), and its failures are absorbed per directory.
288
+ */
289
+ static readonly findRoot: <E, R>(dirs: ReadonlyArray<string>, isRoot: (dir: string) => Effect.Effect<boolean, E, R>) => Effect.Effect<Option.Option<string>, never, R>;
290
+ }
238
291
  //#endregion
239
292
  export { type AscendOptions, type CompileAndExpandOptions, DescendError, type DescendOptions, GlobExpansionError, Walker, compileAndExpand, descend };
240
293
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/walker",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "description": "Path traversal as Effect primitives: ascend a directory chain to the first matching candidate, or descend a tree expanding a glob against the filesystem.",
6
6
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "./package.json": "./package.json"
38
38
  },
39
39
  "peerDependencies": {
40
- "@effected/glob": "~0.2.1",
40
+ "@effected/glob": "^0.2.2",
41
41
  "effect": "4.0.0-beta.101"
42
42
  },
43
43
  "engines": {