@ontrails/regrade 1.0.0-beta.46 → 1.0.0-beta.47

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @ontrails/regrade
2
2
 
3
+ ## 1.0.0-beta.47
4
+
5
+ ### Patch Changes
6
+
7
+ - [`90d394c`](https://github.com/outfitter-dev/trails/commit/90d394c005fdf6b898ba7052d0b56755af0f4954): Derive nested worktree, repository, and submodule collection boundaries in the
8
+ shared Source walker. Regrade and Warden now observe one directly targeted
9
+ working tree per run, and Regrade audit summaries expose boundary skip counts.
10
+
3
11
  ## 1.0.0-beta.46
4
12
 
5
13
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/regrade",
3
- "version": "1.0.0-beta.46",
3
+ "version": "1.0.0-beta.47",
4
4
  "description": "Downstream migration reporting and safe rewrite helpers for Trails.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,13 +27,13 @@
27
27
  "clean": "rm -rf dist *.tsbuildinfo"
28
28
  },
29
29
  "dependencies": {
30
- "@ontrails/core": "^1.0.0-beta.46",
31
- "@ontrails/source": "^1.0.0-beta.46",
32
- "@ontrails/warden": "^1.0.0-beta.46",
30
+ "@ontrails/core": "^1.0.0-beta.47",
31
+ "@ontrails/source": "^1.0.0-beta.47",
32
+ "@ontrails/warden": "^1.0.0-beta.47",
33
33
  "zod": "^4.3.5"
34
34
  },
35
35
  "devDependencies": {
36
- "@ontrails/testing": "^1.0.0-beta.46",
37
- "@ontrails/topography": "^1.0.0-beta.46"
36
+ "@ontrails/testing": "^1.0.0-beta.47",
37
+ "@ontrails/topography": "^1.0.0-beta.47"
38
38
  }
39
39
  }
@@ -4,8 +4,7 @@ import {
4
4
  matchesAnyPathGlob,
5
5
  trail,
6
6
  } from '@ontrails/core';
7
- import { readdirSync } from 'node:fs';
8
- import { join, posix, relative, resolve, sep } from 'node:path';
7
+ import { collectSourceTree } from '@ontrails/source';
9
8
  import { z } from 'zod';
10
9
 
11
10
  /**
@@ -124,46 +123,10 @@ export const classifyDownstreamEntry = (
124
123
  : { action: 'skip', reason: 'unsupported-extension' };
125
124
  };
126
125
 
127
- const toPosixRelative = (root: string, absolutePath: string): string => {
128
- const rel = relative(root, absolutePath);
129
- return sep === posix.sep ? rel : rel.split(sep).join(posix.sep);
130
- };
131
-
132
126
  const isImmutableRegradeHistoryDirectory = (path: string): boolean =>
133
127
  path === '.trails/regrade/history' ||
134
128
  path.endsWith('/.trails/regrade/history');
135
129
 
136
- const direntKind = (dirent: {
137
- isDirectory(): boolean;
138
- isFile(): boolean;
139
- }): DownstreamEntryKind => {
140
- if (dirent.isDirectory()) {
141
- return 'directory';
142
- }
143
- return dirent.isFile() ? 'file' : 'other';
144
- };
145
-
146
- type DirectoryRead =
147
- | {
148
- readonly ok: true;
149
- readonly entries: readonly {
150
- readonly name: string;
151
- readonly kind: DownstreamEntryKind;
152
- }[];
153
- }
154
- | { readonly ok: false };
155
-
156
- const readDirectory = (absoluteDir: string): DirectoryRead => {
157
- try {
158
- const entries = readdirSync(absoluteDir, { withFileTypes: true }).map(
159
- (dirent) => ({ kind: direntKind(dirent), name: dirent.name })
160
- );
161
- return { entries, ok: true };
162
- } catch {
163
- return { ok: false };
164
- }
165
- };
166
-
167
130
  /**
168
131
  * Walk an explicit downstream root and collect candidate source files.
169
132
  *
@@ -175,69 +138,27 @@ const readDirectory = (absoluteDir: string): DirectoryRead => {
175
138
  export const collectDownstreamSources = (
176
139
  root: string,
177
140
  options: DownstreamCollectionOptions = {}
178
- ): DownstreamSourceCollection | null => {
179
- const absoluteRoot = resolve(root);
180
- const rootRead = readDirectory(absoluteRoot);
181
- if (!rootRead.ok) {
182
- return null;
183
- }
184
-
185
- const files: CollectedSource[] = [];
186
- const skipped: SkippedSource[] = [];
187
- const queue: string[] = [absoluteRoot];
188
-
189
- while (queue.length > 0) {
190
- const current = queue.shift() as string;
191
- const read = current === absoluteRoot ? rootRead : readDirectory(current);
192
- if (!read.ok) {
193
- skipped.push({
194
- path: toPosixRelative(absoluteRoot, current),
195
- reason: 'unreadable-directory',
196
- });
197
- continue;
198
- }
199
-
200
- for (const entry of read.entries) {
201
- const absolutePath = join(current, entry.name);
202
- const path = toPosixRelative(absoluteRoot, absolutePath);
141
+ ): DownstreamSourceCollection | null =>
142
+ collectSourceTree(root, {
143
+ classify: ({ kind, name, path }) => {
203
144
  if (matchesAnyPathGlob(path, options.exclude)) {
204
- skipped.push({ path, reason: 'ignored-glob' });
205
- continue;
145
+ return { action: 'skip', reason: 'ignored-glob' };
146
+ }
147
+ if (kind === 'directory' && isImmutableRegradeHistoryDirectory(path)) {
148
+ return { action: 'skip', reason: 'immutable-regrade-history' };
206
149
  }
150
+ const classification = classifyDownstreamEntry(name, kind, options);
207
151
  if (
208
- entry.kind === 'directory' &&
209
- isImmutableRegradeHistoryDirectory(path)
152
+ classification.action === 'collect' &&
153
+ options.include !== undefined &&
154
+ options.include.length > 0 &&
155
+ !matchesAnyPathGlob(path, options.include)
210
156
  ) {
211
- skipped.push({ path, reason: 'immutable-regrade-history' });
212
- continue;
157
+ return { action: 'skip', reason: 'not-included-glob' };
213
158
  }
214
- const classification = classifyDownstreamEntry(
215
- entry.name,
216
- entry.kind,
217
- options
218
- );
219
- if (classification.action === 'collect') {
220
- if (
221
- options.include !== undefined &&
222
- options.include.length > 0 &&
223
- !matchesAnyPathGlob(path, options.include)
224
- ) {
225
- skipped.push({ path, reason: 'not-included-glob' });
226
- } else {
227
- files.push({ absolutePath, path });
228
- }
229
- } else if (classification.action === 'recurse') {
230
- queue.push(absolutePath);
231
- } else {
232
- skipped.push({ path, reason: classification.reason });
233
- }
234
- }
235
- }
236
-
237
- files.sort((a, b) => a.path.localeCompare(b.path));
238
- skipped.sort((a, b) => a.path.localeCompare(b.path));
239
- return { files, root: absoluteRoot, skipped };
240
- };
159
+ return classification;
160
+ },
161
+ });
241
162
 
242
163
  export const collectDownstreamSourcesInput = z.object({
243
164
  exclude: z