@effected/walker 0.21.0 → 0.23.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.
@@ -1,5 +1,6 @@
1
1
  import { resolveSegments } from "./segments.js";
2
2
  import { trimTrailing } from "./trimTrailing.js";
3
+ import { isPinnedAbsolute } from "./absolute.js";
3
4
 
4
5
  // src/internal/descendMachine.ts
5
6
  /** Directory names never descended into unless the caller overrides `prune`. */
@@ -9,7 +10,8 @@ const isSlash = (character) => character === "/";
9
10
  /** Shared empty ancestor chain: frames under `followSymlinks: false` never consult theirs. */
10
11
  const NO_ANCESTORS = Object.freeze([]);
11
12
  /** The walk. */
12
- function* descendMachine(pattern, options, join) {
13
+ function* descendMachine(pattern, options, path) {
14
+ const join = path.join;
13
15
  /** Yields one request; the answer is typed by the request's tag. */
14
16
  function* ask(request) {
15
17
  return yield request;
@@ -19,6 +21,19 @@ function* descendMachine(pattern, options, join) {
19
21
  _tag: "Defect",
20
22
  message: `Walker.descend: maxDepth must be a positive integer, received ${maxDepth}`
21
23
  };
24
+ if (options.cwd.includes("\0") || !isPinnedAbsolute(options.cwd, path)) return {
25
+ _tag: "Defect",
26
+ message: `Walker.descend: cwd must be an absolute path, received ${JSON.stringify(options.cwd)}`
27
+ };
28
+ const match = options.match ?? "file";
29
+ if (match !== "file" && match !== "directory" && match !== "any") return {
30
+ _tag: "Defect",
31
+ message: `Walker.descend: match must be "file", "directory" or "any", received ${JSON.stringify(match)}`
32
+ };
33
+ const matchFiles = match !== "directory";
34
+ const matchDirectories = match !== "file";
35
+ /** Canonical paths of the directories already answered: a root's base may also be an entry of another root. */
36
+ const answeredDirectories = /* @__PURE__ */ new Set();
22
37
  const prune = new Set(options.prune ?? DEFAULT_PRUNE);
23
38
  const boundaries = options.boundaries ?? [];
24
39
  const onUnreadable = options.onUnreadable ?? "fail";
@@ -93,7 +108,8 @@ function* descendMachine(pattern, options, join) {
93
108
  _tag: "Stat",
94
109
  path: join(options.cwd, pattern.source)
95
110
  });
96
- return done(answer?.kind === "File" ? [{
111
+ const wanted = answer?.kind === "File" && matchFiles || answer?.kind === "Directory" && matchDirectories && segments.length > 0;
112
+ return done(answer !== void 0 && wanted ? [{
97
113
  path: pattern.source,
98
114
  info: answer.info
99
115
  }] : []);
@@ -122,7 +138,7 @@ function* descendMachine(pattern, options, join) {
122
138
  canonical: canonical === "" ? "" : `${canonical}/`
123
139
  };
124
140
  });
125
- /** The file at canonical `relative`, in the first spelling the pattern matches it under, or `undefined`. */
141
+ /** The entry at canonical `relative`, in the first spelling the pattern matches it under, or `undefined`. */
126
142
  const matchedAs = (relative) => {
127
143
  for (const { spelling, canonical } of spelled) {
128
144
  if (canonical !== "" && !relative.startsWith(canonical)) continue;
@@ -130,6 +146,17 @@ function* descendMachine(pattern, options, join) {
130
146
  if (pattern.matches(candidate)) return candidate;
131
147
  }
132
148
  };
149
+ /**
150
+ * The directory at canonical `relative`, in the first spelling the pattern
151
+ * matches it under — bare, or with a trailing `/` (a glob's directory-only
152
+ * form) — or `undefined`. Each directory is answered once.
153
+ */
154
+ const matchedDirectoryAs = (relative) => {
155
+ if (answeredDirectories.has(relative)) return void 0;
156
+ const answer = matchedAs(relative) ?? matchedAs(`${relative}/`);
157
+ if (answer !== void 0) answeredDirectories.add(relative);
158
+ return answer;
159
+ };
133
160
  const baseSegments = resolveSegments(rawBase.split("/"));
134
161
  if (baseSegments === void 0) return void 0;
135
162
  const base = baseSegments.join("/");
@@ -137,10 +164,11 @@ function* descendMachine(pattern, options, join) {
137
164
  const own = spelled.find(({ canonical }) => canonical === baseCanonical)?.spelling ?? baseCanonical;
138
165
  const shown = (relative) => relative === base ? trimTrailing(own, isSlash) : own + relative.slice(baseCanonical.length);
139
166
  const absoluteBase = base === "" ? options.cwd : join(options.cwd, base);
140
- if ((yield* ask({
167
+ const baseStat = yield* ask({
141
168
  _tag: "Stat",
142
169
  path: absoluteBase
143
- }))?.kind !== "Directory") return void 0;
170
+ });
171
+ if (baseStat?.kind !== "Directory") return void 0;
144
172
  if (yield* blocked(baseSegments, baseSegments.length, baseSegments.length - 1)) return void 0;
145
173
  let baseAncestors = NO_ANCESTORS;
146
174
  if (followSymlinks) {
@@ -149,6 +177,13 @@ function* descendMachine(pattern, options, join) {
149
177
  if (real === void 0) return void 0;
150
178
  baseAncestors = [real];
151
179
  }
180
+ if (matchDirectories && base !== "") {
181
+ const answer = matchedDirectoryAs(base);
182
+ if (answer !== void 0) results.push({
183
+ path: answer,
184
+ info: baseStat.info
185
+ });
186
+ }
152
187
  const frames = [{
153
188
  relative: base,
154
189
  absolute: absoluteBase,
@@ -186,13 +221,20 @@ function* descendMachine(pattern, options, join) {
186
221
  }) : own;
187
222
  if (stat === void 0) continue;
188
223
  if (stat.kind === "File") {
189
- const path = matchedAs(relative);
190
- if (path !== void 0) results.push({
191
- path,
224
+ const answer = matchFiles ? matchedAs(relative) : void 0;
225
+ if (answer !== void 0) results.push({
226
+ path: answer,
192
227
  info: stat.info
193
228
  });
194
229
  continue;
195
230
  }
231
+ if (stat.kind === "Directory" && matchDirectories) {
232
+ const answer = matchedDirectoryAs(relative);
233
+ if (answer !== void 0) results.push({
234
+ path: answer,
235
+ info: stat.info
236
+ });
237
+ }
196
238
  if (stat.kind !== "Directory" || !deep) continue;
197
239
  if (prune.has(entry)) continue;
198
240
  let ancestors = frame.ancestors;
@@ -0,0 +1,71 @@
1
+ import { Effect, FileSystem } from "effect";
2
+
3
+ // src/internal/upwardDrivers.ts
4
+ /** Runs a machine against core's `FileSystem`. A machine's `Defect` dies. */
5
+ const runUpward = (machine) => Effect.gen(function* () {
6
+ const fs = yield* FileSystem.FileSystem;
7
+ const answer = (request) => {
8
+ if (request._tag === "Exists") return fs.exists(request.path).pipe(Effect.orElseSucceed(() => false));
9
+ if (request._tag === "Present") return fs.lstat(request.path).pipe(Effect.map(() => ({ _tag: "Present" })), Effect.catch((error) => Effect.succeed(error.reason._tag === "NotFound" ? { _tag: "NotFound" } : {
10
+ _tag: "Failed",
11
+ cause: error
12
+ })));
13
+ return fs.realPath(request.path).pipe(Effect.map((value) => ({
14
+ _tag: "Ok",
15
+ value
16
+ })), Effect.catch((error) => Effect.succeed(error.reason._tag === "NotFound" ? {
17
+ _tag: "NotFound",
18
+ cause: error
19
+ } : {
20
+ _tag: "Failed",
21
+ cause: error
22
+ })));
23
+ };
24
+ let step = machine.next();
25
+ while (step.done !== true) step = machine.next(yield* answer(step.value));
26
+ const result = step.value;
27
+ if (result._tag === "Defect") return yield* Effect.die(new Error(result.message));
28
+ return result.value;
29
+ });
30
+ const isNotFound = (error) => typeof error === "object" && error !== null && error.code === "ENOENT";
31
+ /** Runs a machine against a synchronous port. A machine's `Defect` throws. */
32
+ const runUpwardSync = (machine, fileSystem) => {
33
+ const answer = (request) => {
34
+ if (request._tag === "Exists") try {
35
+ fileSystem.stat(request.path);
36
+ return true;
37
+ } catch {
38
+ return false;
39
+ }
40
+ if (request._tag === "Present") try {
41
+ fileSystem.lstat(request.path);
42
+ return { _tag: "Present" };
43
+ } catch (cause) {
44
+ return isNotFound(cause) ? { _tag: "NotFound" } : {
45
+ _tag: "Failed",
46
+ cause
47
+ };
48
+ }
49
+ try {
50
+ return {
51
+ _tag: "Ok",
52
+ value: fileSystem.realPath(request.path)
53
+ };
54
+ } catch (cause) {
55
+ return isNotFound(cause) ? {
56
+ _tag: "NotFound",
57
+ cause
58
+ } : {
59
+ _tag: "Failed",
60
+ cause
61
+ };
62
+ }
63
+ };
64
+ let step = machine.next();
65
+ while (step.done !== true) step = machine.next(answer(step.value));
66
+ const result = step.value;
67
+ if (result._tag === "Defect") throw new Error(result.message);
68
+ return result.value;
69
+ };
70
+
71
+ export { runUpward, runUpwardSync };
@@ -0,0 +1,252 @@
1
+ import { trimTrailing } from "./trimTrailing.js";
2
+ import { isCurrentDriveRooted, isPinnedAbsolute } from "./absolute.js";
3
+
4
+ // src/internal/upwardMachine.ts
5
+ /** Asks for the real path at `path`. */
6
+ function* realPath(path) {
7
+ return yield {
8
+ _tag: "RealPath",
9
+ path
10
+ };
11
+ }
12
+ /** Asks whether anything — a dangling link included — lives at `path` itself. */
13
+ function* present(path) {
14
+ return yield {
15
+ _tag: "Present",
16
+ path
17
+ };
18
+ }
19
+ /** Asks whether `path` exists. */
20
+ function* exists(path) {
21
+ return yield {
22
+ _tag: "Exists",
23
+ path
24
+ };
25
+ }
26
+ const done = (value) => ({
27
+ _tag: "Done",
28
+ value
29
+ });
30
+ const defect = (message) => ({
31
+ _tag: "Defect",
32
+ message
33
+ });
34
+ /**
35
+ * The lexical chain from `start` toward the root, nearest first — `ascend`'s
36
+ * whole algorithm. Pure: it reads no filesystem, so it is not a generator.
37
+ * `name` prefixes the defect messages, so each public form reports itself.
38
+ */
39
+ const ascendChain = (name, start, options, path) => {
40
+ const maxDepth = options?.maxDepth ?? 256;
41
+ if (!Number.isInteger(maxDepth) || maxDepth < 1) return defect(`${name}: maxDepth must be a positive integer, received ${maxDepth}`);
42
+ if (options?.stopAt !== void 0 && !isPinnedAbsolute(options.stopAt, path)) return defect(`${name}: stopAt must be an absolute path, received ${JSON.stringify(options.stopAt)} (ascending from ${JSON.stringify(start)})`);
43
+ const ceiling = options?.stopAt === void 0 ? void 0 : path.resolve(options.stopAt);
44
+ const dirs = [];
45
+ let current = start;
46
+ for (let depth = 0; depth < maxDepth; depth++) {
47
+ dirs.push(current);
48
+ if (ceiling !== void 0 && (current === ceiling || path.resolve(current) === ceiling)) break;
49
+ const parent = path.dirname(current);
50
+ if (parent === current) break;
51
+ current = parent;
52
+ }
53
+ return done(dirs);
54
+ };
55
+ /** The real path at `path`, or `undefined` when it cannot be resolved for any reason. */
56
+ function* realPathOrUndefined(path) {
57
+ const answer = yield* realPath(path);
58
+ return answer._tag === "Ok" ? answer.value : void 0;
59
+ }
60
+ /**
61
+ * `ascendWithin`: the lexical chain, stopped at the nearest ancestor whose real
62
+ * path is the ceiling's. `ceiling === undefined` is `Option.none()` — the chain
63
+ * runs to the root exactly as `ascend` gives it.
64
+ */
65
+ function* ascendWithinMachine(name, start, ceiling, options, path) {
66
+ const maxDepth = options?.maxDepth === void 0 ? {} : { maxDepth: options.maxDepth };
67
+ if (ceiling === void 0) return ascendChain(name, start, maxDepth, path);
68
+ if (!isPinnedAbsolute(ceiling, path)) return defect(`${name}: ceiling must be an absolute path, received ${JSON.stringify(ceiling)} (ascending from ${JSON.stringify(start)})`);
69
+ const lexical = ascendChain(name, start, {
70
+ ...maxDepth,
71
+ stopAt: ceiling
72
+ }, path);
73
+ if (lexical._tag === "Defect") return lexical;
74
+ const dirs = lexical.value;
75
+ const last = dirs.at(-1);
76
+ if (last !== void 0 && path.resolve(last) === path.resolve(ceiling)) return lexical;
77
+ const physical = yield* realPathOrUndefined(ceiling);
78
+ if (physical === void 0) return lexical;
79
+ for (let index = 0; index < dirs.length; index += 1) if ((yield* realPathOrUndefined(dirs[index] ?? "")) === physical) return done(dirs.slice(0, index + 1));
80
+ return lexical;
81
+ }
82
+ /**
83
+ * `ascendToMarker`: the lexical chain, stopped at the nearest directory that
84
+ * holds any of `markers`, inclusive. Probes stop at the hit — nothing above it
85
+ * is ever touched.
86
+ */
87
+ function* ascendToMarkerMachine(name, start, markers, options, path) {
88
+ if (markers.length === 0) return defect(`${name}: markers must name at least one entry (ascending from ${JSON.stringify(start)})`);
89
+ if (markers.find((marker) => marker === "") !== void 0) return defect(`${name}: a marker must be a non-empty entry name (ascending from ${JSON.stringify(start)})`);
90
+ const chain = ascendChain(name, start, options?.maxDepth === void 0 ? {} : { maxDepth: options.maxDepth }, path);
91
+ if (chain._tag === "Defect") return chain;
92
+ const dirs = chain.value;
93
+ for (let index = 0; index < dirs.length; index += 1) {
94
+ const dir = dirs[index] ?? "";
95
+ for (const marker of markers) if (yield* exists(path.join(dir, marker))) return done(dirs.slice(0, index + 1));
96
+ }
97
+ return chain;
98
+ }
99
+ /** `findUpward`: the first existing candidate, directory-major, or `undefined`. */
100
+ function* findUpwardMachine(dirs, candidatesFor) {
101
+ const candidates = [];
102
+ for (const dir of dirs) candidates.push(...candidatesFor(dir));
103
+ for (const candidate of candidates) if (yield* exists(candidate)) return done(candidate);
104
+ return done(void 0);
105
+ }
106
+ /**
107
+ * `findUpwardAll`: every existing candidate of the nearest directory with any
108
+ * hit, in candidate order — that directory's candidates are ALL probed, so a
109
+ * second hit (an ambiguity) is never hidden; nothing above it is probed.
110
+ */
111
+ function* findUpwardAllMachine(dirs, candidatesFor) {
112
+ const expanded = dirs.map((dir) => ({
113
+ dir,
114
+ candidates: candidatesFor(dir)
115
+ }));
116
+ for (const { dir, candidates } of expanded) {
117
+ const found = [];
118
+ for (const candidate of candidates) if (yield* exists(candidate)) found.push(candidate);
119
+ const [first, ...rest] = found;
120
+ if (first !== void 0) return done({
121
+ dir,
122
+ found: [first, ...rest]
123
+ });
124
+ }
125
+ return done(void 0);
126
+ }
127
+ /** A segment of dots and spaces only, which win32 trims to nothing or to `..`. */
128
+ const DOTS_AND_SPACES = /^[. ]+$/;
129
+ /** A win32 reserved device name, with or without an extension, in any case. */
130
+ const DEVICE_NAME = /^(?:con|prn|aux|nul|com[0-9\u00b9\u00b2\u00b3]|lpt[0-9\u00b9\u00b2\u00b3]|conin\$|conout\$) *(?:\..*)?$/i;
131
+ /** A win32 drive designator at the start of a path: `C:`, `C:x`, `C:\x`. */
132
+ const DRIVE = /^[A-Za-z]:/;
133
+ /** Whether win32 reads a segment as something other than a plain entry (`.` and `..` are handled before). */
134
+ const isWin32Reserved = (segment) => segment.includes(":") || DOTS_AND_SPACES.test(segment) || DEVICE_NAME.test(segment);
135
+ /**
136
+ * Resolve `absolute` physically, segment by segment, the way the operating
137
+ * system would: a `..` after a link climbs out of the link's TARGET, never
138
+ * lexically out of the link. A first `RealPath` on the whole path settles the
139
+ * common case in one request when no `..` is present (a lexical-first
140
+ * `realpath` — node's JS `fs.realpath` — would otherwise collapse `link/..`
141
+ * before resolving the link). A path, or tail, that does not exist yet is
142
+ * resolved lexically from its real existing ancestor — but `NotFound` from a
143
+ * real-path request is NOT proof of absence: a dangling link answers it too,
144
+ * and a write through one creates its target wherever it points. So every
145
+ * `NotFound` segment is checked with `Present`, and only "nothing lives here"
146
+ * continues lexically; a dangling link (or an entry that appeared between the
147
+ * two requests) is unresolvable, its target unverified. Any failure other
148
+ * than `NotFound` makes the path unresolvable too. Fail closed throughout.
149
+ */
150
+ function* resolvePhysical(absolute, path, win32, checkSegments, requireExisting = false) {
151
+ let root = absolute;
152
+ for (let parent = path.dirname(root); parent !== root; parent = path.dirname(root)) root = parent;
153
+ const segments = (absolute.startsWith(root) ? absolute.slice(root.length) : absolute).split(win32 ? /[\\/]/ : "/").filter((segment) => segment !== "");
154
+ if (checkSegments && win32) {
155
+ for (const segment of segments) if (segment !== "." && segment !== ".." && isWin32Reserved(segment)) return {
156
+ _tag: "Refused",
157
+ reason: "reservedName"
158
+ };
159
+ }
160
+ const isSep = (character) => character === path.sep || win32 && character === "/";
161
+ const real = (target) => realPath(target);
162
+ if (!segments.includes("..")) {
163
+ const whole = yield* real(absolute);
164
+ if (whole._tag === "Ok") return whole;
165
+ if (whole._tag === "Failed") return {
166
+ _tag: "Unresolvable",
167
+ cause: whole.cause
168
+ };
169
+ }
170
+ const rootAnswer = yield* real(root);
171
+ if (rootAnswer._tag === "Failed") return {
172
+ _tag: "Unresolvable",
173
+ cause: rootAnswer.cause
174
+ };
175
+ let current = rootAnswer._tag === "Ok" ? rootAnswer.value : root;
176
+ for (const segment of segments) {
177
+ if (segment === ".") continue;
178
+ if (segment === "..") {
179
+ current = path.dirname(current);
180
+ continue;
181
+ }
182
+ const next = `${trimTrailing(current, isSep)}${path.sep}${segment}`;
183
+ const answer = yield* real(next);
184
+ if (answer._tag === "Failed") return {
185
+ _tag: "Unresolvable",
186
+ cause: answer.cause
187
+ };
188
+ if (answer._tag === "Ok") {
189
+ current = answer.value;
190
+ continue;
191
+ }
192
+ const entry = yield* present(next);
193
+ if (entry._tag === "Failed") return {
194
+ _tag: "Unresolvable",
195
+ cause: entry.cause
196
+ };
197
+ if (entry._tag === "Present") return {
198
+ _tag: "Unresolvable",
199
+ cause: answer.cause
200
+ };
201
+ if (requireExisting) return {
202
+ _tag: "Refused",
203
+ reason: "outside"
204
+ };
205
+ current = next;
206
+ }
207
+ return {
208
+ _tag: "Ok",
209
+ value: current
210
+ };
211
+ }
212
+ /**
213
+ * `confineReal`: the candidate's real path when it lies strictly inside the
214
+ * base's real path, or why not. A relative `base` is a wiring defect.
215
+ */
216
+ function* confineRealMachine(name, base, candidate, path, within = false) {
217
+ if (base.includes("\0") || !isPinnedAbsolute(base, path)) return defect(`${name}: base must be an absolute path, received ${JSON.stringify(base)}`);
218
+ const refuse = (reason) => done({
219
+ _tag: "Refused",
220
+ reason
221
+ });
222
+ if (candidate.includes("\0")) return refuse("nulByte");
223
+ if (candidate === "") return refuse("empty");
224
+ if (within && !path.isAbsolute(candidate)) return refuse("absolute");
225
+ const win32 = path.sep === "\\";
226
+ const isSep = (character) => character === path.sep || win32 && character === "/";
227
+ if (win32 && DRIVE.test(candidate) && !path.isAbsolute(candidate)) return refuse("absolute");
228
+ if (isCurrentDriveRooted(candidate, path.sep)) return refuse("absolute");
229
+ const full = path.isAbsolute(candidate) ? candidate : `${trimTrailing(base, isSep)}${path.sep}${candidate}`;
230
+ const baseReal = yield* resolvePhysical(base, path, win32, false);
231
+ if (baseReal._tag !== "Ok") return done(baseReal);
232
+ const candidateReal = yield* resolvePhysical(full, path, win32, true, within !== false && within.requireExisting);
233
+ if (candidateReal._tag !== "Ok") return done(candidateReal);
234
+ const root = trimTrailing(baseReal.value, isSep);
235
+ const resolved = trimTrailing(candidateReal.value, isSep);
236
+ if (within && resolved === root) return done({
237
+ _tag: "Ok",
238
+ value: ""
239
+ });
240
+ if (resolved === root) return refuse("empty");
241
+ if (!resolved.startsWith(`${root}${path.sep}`)) return refuse("outside");
242
+ if (within) return done({
243
+ _tag: "Ok",
244
+ value: resolved.slice(root.length + 1).split(path.sep).join("/")
245
+ });
246
+ return done({
247
+ _tag: "Ok",
248
+ value: candidateReal.value
249
+ });
250
+ }
251
+
252
+ export { ascendChain, ascendToMarkerMachine, ascendWithinMachine, confineRealMachine, findUpwardAllMachine, findUpwardMachine };
package/node-sync.d.ts CHANGED
@@ -43,17 +43,30 @@ interface WalkerSyncFileSystem {
43
43
  readonly realPath: (path: string) => string;
44
44
  }
45
45
  /**
46
- * The synchronous path operation the sync walk needs — a structural subset of
47
- * `node:path`, so the module itself (or its `win32` / `posix` variant)
46
+ * The synchronous path operations the sync forms need — a structural subset
47
+ * of `node:path`, so the module itself (or its `win32` / `posix` variant)
48
48
  * satisfies it. It shapes only the absolute paths handed to the
49
49
  * {@link WalkerSyncFileSystem}; matching is POSIX-relative by the glob
50
50
  * contract.
51
51
  *
52
+ * @remarks
53
+ * The members mirror core's `Path` service, so a sync form judges
54
+ * absoluteness, ascends and compares ceilings exactly as its `Effect` twin
55
+ * does under the `Path` layer of the same platform.
56
+ *
52
57
  * @public
53
58
  */
54
59
  interface WalkerSyncPath {
60
+ /** The implementation's separator (`path.sep`). */
61
+ readonly sep: string;
55
62
  /** Join segments with the implementation's separator (like `path.join`). */
56
63
  readonly join: (...segments: ReadonlyArray<string>) => string;
64
+ /** The parent directory of a path (like `path.dirname`); a fixpoint at the root. */
65
+ readonly dirname: (path: string) => string;
66
+ /** Resolve segments to a normalized path (like `path.resolve`). */
67
+ readonly resolve: (...segments: ReadonlyArray<string>) => string;
68
+ /** Whether a path is absolute under this implementation (like `path.isAbsolute`). */
69
+ readonly isAbsolute: (path: string) => boolean;
57
70
  }
58
71
  /**
59
72
  * The platform half of every sync walk.
package/node-sync.js CHANGED
@@ -41,7 +41,13 @@ const nodeWalkerFileSystem = {
41
41
  *
42
42
  * @public
43
43
  */
44
- const nodeWalkerPath = { join: path.join };
44
+ const nodeWalkerPath = {
45
+ sep: path.sep,
46
+ join: path.join,
47
+ dirname: path.dirname,
48
+ resolve: path.resolve,
49
+ isAbsolute: path.isAbsolute
50
+ };
45
51
  /**
46
52
  * Both bindings, to spread into `descendSync` options.
47
53
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/walker",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
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": [
@@ -42,7 +42,7 @@
42
42
  "./package.json": "./package.json"
43
43
  },
44
44
  "peerDependencies": {
45
- "@effected/glob": "^0.15.0",
45
+ "@effected/glob": "^0.16.0",
46
46
  "effect": "^4.0.0"
47
47
  },
48
48
  "engines": {
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.59.4"
8
+ "packageVersion": "7.60.0"
9
9
  }
10
10
  ]
11
11
  }