@karmaniverous/stan-cli 0.12.1 → 0.12.2

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 (2) hide show
  1. package/dist/cli/stan.js +81 -93
  2. package/package.json +1 -1
package/dist/cli/stan.js CHANGED
@@ -32022,27 +32022,28 @@ const isSubtreePattern = (s) => {
32022
32022
  const p = posix(s.trim());
32023
32023
  return p.endsWith('/**') || p.endsWith('/*');
32024
32024
  };
32025
- /** Extract the "tail" (after last '/') from a glob (e.g., '**\/*.test.ts' -\> '*.test.ts'). */
32026
- const globTail = (s) => {
32027
- const p = posix(s.trim());
32028
- const idx = p.lastIndexOf('/');
32029
- return idx >= 0 ? p.slice(idx + 1) : p;
32030
- };
32031
32025
  /** Normalize subtree roots from a list of exclude patterns. */
32032
32026
  const collectSubtreeRoots = (patterns) => (patterns ?? [])
32033
32027
  .filter((p) => isSubtreePattern(p))
32034
32028
  .map(stripGlobTail)
32035
32029
  .filter((r) => r.length > 0);
32036
- /** Collect leaf‑glob tails (e.g., '**\/*.test.ts' -\> '*.test.ts') from a list of exclude patterns. */
32037
- const collectLeafGlobTails = (patterns) => (patterns ?? [])
32038
- .filter((p) => !isSubtreePattern(p))
32039
- .map(globTail)
32040
- .filter((t) => t.length > 0);
32041
32030
  const isUnder = (childRel, root) => {
32042
32031
  const c = posix(childRel);
32043
32032
  const r = posix(root);
32044
32033
  return c === r || c.startsWith(r.length ? r + '/' : '');
32045
32034
  };
32035
+ const toSubtreeGlob = (root) => `${posix(root)}/**`;
32036
+ const segmentUnderRoot = (root, p) => {
32037
+ const r = posix(root);
32038
+ const full = posix(p);
32039
+ if (!isUnder(full, r))
32040
+ return null;
32041
+ const rest = full.slice(r.length).replace(/^\/+/, '');
32042
+ if (!rest.length)
32043
+ return null;
32044
+ const first = rest.split('/')[0];
32045
+ return first && first.length ? first : null;
32046
+ };
32046
32047
  const readFacetMeta = async (cwd, stanPath) => {
32047
32048
  const abs = toAbs(cwd, path.join(stanPath, 'system', 'facet.meta.json'));
32048
32049
  const meta = await safeReadJson(abs);
@@ -32096,14 +32097,10 @@ const computeFacetOverlay = async (input) => {
32096
32097
  const anchorsOverlaySet = new Set();
32097
32098
  // Final excludes overlay entries (subtree roots only; leaf-globs are not propagated here).
32098
32099
  const excludesOverlayArr = [];
32099
- // Track subtree-root entries for enabled-wins filtering.
32100
- const excludesOverlayRoots = [];
32101
32100
  const autosuspended = [];
32102
32101
  const anchorsKeptCounts = {};
32103
32102
  // Track per-facet inactive subtree roots for overlap-kept diagnostics.
32104
32103
  const inactiveEntries = [];
32105
- // Collect leaf‑glob tails from active facets (protected patterns).
32106
- const activeLeafTails = new Set();
32107
32104
  // Precompute active subtree roots across all facets (for tie-breakers and scoped anchors).
32108
32105
  const activeRoots = new Set();
32109
32106
  for (const name of facetNames) {
@@ -32112,15 +32109,7 @@ const computeFacetOverlay = async (input) => {
32112
32109
  if (isActive)
32113
32110
  for (const r of exRoots)
32114
32111
  activeRoots.add(posix(r));
32115
- // Also collect leaf‑glob tails for active facets so they can be protected
32116
- // under inactive subtree roots (enabled‑wins across leaf‑glob vs subtree).
32117
- if (isActive) {
32118
- for (const tail of collectLeafGlobTails(meta[name].exclude))
32119
- activeLeafTails.add(tail);
32120
- }
32121
32112
  }
32122
- // Collect leaf-glob tails from inactive facets (for scoped anchors under active roots).
32123
- const inactiveLeafTails = new Set();
32124
32113
  // Always include all anchors (keep docs breadcrumbs visible even when overlay off)
32125
32114
  for (const name of facetNames) {
32126
32115
  const def = defOf(name);
@@ -32177,7 +32166,6 @@ const computeFacetOverlay = async (input) => {
32177
32166
  .filter(isSubtreePattern)
32178
32167
  .map(stripGlobTail)
32179
32168
  .filter(Boolean);
32180
- const leafGlobs = excludes.filter((p) => !isSubtreePattern(p));
32181
32169
  const inc = Array.isArray(def.include) ? def.include.map(posix) : [];
32182
32170
  // Count anchors present on disk (for metadata)
32183
32171
  anchorsKeptCounts[name] = inc.filter((a) => existsSync(toAbs(cwd, a))).length;
@@ -32205,75 +32193,80 @@ const computeFacetOverlay = async (input) => {
32205
32193
  if (!root)
32206
32194
  continue;
32207
32195
  inactiveEntries.push({ facet: name, root });
32208
- excludesOverlayRoots.push(root.endsWith('/') ? root : root);
32209
- }
32210
- // Collect leaf-glob tails for scoped re-inclusions under active roots.
32211
- for (const g of leafGlobs) {
32212
- const tail = globTail(g);
32213
- if (tail)
32214
- inactiveLeafTails.add(tail);
32215
32196
  }
32216
32197
  }
32217
- // Subtree tie-breaker: enabled facet wins (drop inactive roots that equal/overlap with active roots).
32198
+ // Nested structural facets: carve-out inactive roots that contain active descendant roots.
32199
+ // - Keep exact-match "enabled wins" behavior (drop inactive root when the same root is active).
32200
+ // - If an inactive root contains one or more active descendant subtree roots, exclude all
32201
+ // immediate children under the inactive root that are NOT ancestors of any active root.
32202
+ // This expresses "B on, rest of A off" without using anchors as filter machinery.
32218
32203
  const overlapKeptCounts = {};
32219
- if (inactiveEntries.length > 0 && activeRoots.size > 0) {
32220
- const act = Array.from(activeRoots);
32221
- const keptRoots = [];
32222
- const keptEntries = [];
32223
- for (const entry of inactiveEntries) {
32224
- const r = entry.root;
32225
- const drop = act.some((ar) => ar === r || isUnder(r, ar) || isUnder(ar, r));
32226
- if (!drop) {
32227
- keptRoots.push(r);
32228
- keptEntries.push(entry);
32229
- }
32230
- }
32231
- // Replace with filtered roots only.
32232
- excludesOverlayArr.length = 0;
32233
- excludesOverlayArr.push(...keptRoots);
32234
- // Count kept roots per facet
32235
- for (const { facet } of keptEntries) {
32236
- overlapKeptCounts[facet] = (overlapKeptCounts[facet] ?? 0) + 1;
32237
- }
32238
- }
32239
- else {
32240
- // No filtering occurred (no active roots or no excludes); consider all inactive entries as kept.
32241
- for (const { facet } of inactiveEntries) {
32242
- overlapKeptCounts[facet] = (overlapKeptCounts[facet] ?? 0) + 1;
32243
- }
32244
- // Append the raw subtree roots only.
32245
- const uniq = Array.from(new Set(excludesOverlayRoots));
32246
- excludesOverlayArr.length = 0;
32247
- excludesOverlayArr.push(...uniq);
32248
- }
32249
- // Enabled‑wins for leaf‑glob patterns: protect ACTIVE facets' leaf‑glob tails
32250
- // under any remaining inactive subtree roots by adding scoped anchors
32251
- // "<inactiveRoot>/**/<tail>" so those files remain visible.
32252
- if (activeLeafTails.size > 0 && excludesOverlayArr.length > 0) {
32204
+ const activeRootsArr = Array.from(activeRoots);
32205
+ const carveOutOrExcludeRoot = async (root) => {
32206
+ const protectedRoots = activeRootsArr.filter((ar) => ar !== root && isUnder(ar, root));
32207
+ if (protectedRoots.length === 0)
32208
+ return [toSubtreeGlob(root)];
32209
+ // Compute which immediate child entries are "keepers".
32210
+ const keep = new Set();
32211
+ for (const pr of protectedRoots) {
32212
+ const seg = segmentUnderRoot(root, pr);
32213
+ if (seg)
32214
+ keep.add(seg);
32215
+ }
32216
+ // Enumerate immediate children under the inactive root and exclude everything
32217
+ // except the keeper segments. Keep deterministic output ordering.
32218
+ const abs = toAbs(cwd, root);
32219
+ let dirents = null;
32253
32220
  try {
32254
- for (const root of excludesOverlayArr) {
32255
- for (const tail of activeLeafTails) {
32256
- const scoped = posix(`${root}/**/${tail}`);
32257
- anchorsOverlaySet.add(scoped);
32258
- }
32259
- }
32221
+ dirents = (await readdir(abs, { withFileTypes: true }));
32260
32222
  }
32261
32223
  catch {
32262
- /* best-effort */
32224
+ dirents = null;
32263
32225
  }
32264
- }
32265
- // Leaf-glob scoped re-inclusion: add anchors "<activeRoot>/**/<tail>" for every collected tail.
32266
- if (inactiveLeafTails.size > 0 && activeRoots.size > 0) {
32267
- for (const ar of activeRoots) {
32268
- for (const tail of inactiveLeafTails) {
32269
- const scoped = posix(`${ar}/**/${tail}`);
32270
- anchorsOverlaySet.add(scoped);
32226
+ // If we can't enumerate, fall back to excluding the full root and
32227
+ // anchor-rescuing the protected roots. This preserves correctness without
32228
+ // reintroducing leaf-glob anchor tricks.
32229
+ if (!dirents) {
32230
+ try {
32231
+ for (const pr of protectedRoots)
32232
+ anchorsOverlaySet.add(toSubtreeGlob(pr));
32271
32233
  }
32234
+ catch {
32235
+ /* best-effort */
32236
+ }
32237
+ return [toSubtreeGlob(root)];
32238
+ }
32239
+ const entries = dirents
32240
+ .map((d) => ({ name: d.name, isDir: d.isDirectory() }))
32241
+ .sort((a, b) => a.name.localeCompare(b.name));
32242
+ const out = [];
32243
+ for (const e of entries) {
32244
+ if (keep.has(e.name))
32245
+ continue;
32246
+ const rel = posix(path.join(root, e.name));
32247
+ out.push(e.isDir ? toSubtreeGlob(rel) : rel);
32272
32248
  }
32249
+ return out;
32250
+ };
32251
+ if (inactiveEntries.length > 0) {
32252
+ const patterns = new Set();
32253
+ for (const entry of inactiveEntries) {
32254
+ const r = entry.root;
32255
+ // Enabled-wins (exact match only): if the same subtree root is active, do not apply the inactive drop.
32256
+ if (activeRoots.has(r))
32257
+ continue;
32258
+ overlapKeptCounts[entry.facet] =
32259
+ (overlapKeptCounts[entry.facet] ?? 0) + 1;
32260
+ const ps = await carveOutOrExcludeRoot(r);
32261
+ for (const p of ps)
32262
+ patterns.add(posix(p));
32263
+ }
32264
+ excludesOverlayArr.length = 0;
32265
+ excludesOverlayArr.push(...Array.from(patterns).sort((a, b) => a.localeCompare(b)));
32273
32266
  }
32274
32267
  // Deduplicate anchors overlay
32275
32268
  const anchorsOverlay = Array.from(anchorsOverlaySet);
32276
- // Excludes overlay contains subtree roots only (already deduped above).
32269
+ // Excludes overlay contains engine-ready patterns (subtree globs and/or exact paths).
32277
32270
  return {
32278
32271
  enabled: true,
32279
32272
  excludesOverlay: excludesOverlayArr,
@@ -32285,11 +32278,6 @@ const computeFacetOverlay = async (input) => {
32285
32278
  };
32286
32279
  };
32287
32280
 
32288
- const hasGlob = (s) => s.includes('*') || s.includes('?') || s.includes('[');
32289
- const ensureSubtreeGlob = (p) => {
32290
- const s = p.replace(/\/+$/, '');
32291
- return hasGlob(s) ? s : `${s}/**`;
32292
- };
32293
32281
  const toPosix = (p) => p.replace(/\\+/g, '/');
32294
32282
  const buildOverlayInputs = async (args) => {
32295
32283
  const { cwd, stanPath, enabled, activateNames, deactivateNames } = args;
@@ -32317,12 +32305,9 @@ const buildOverlayInputs = async (args) => {
32317
32305
  };
32318
32306
  }
32319
32307
  const shouldMap = enabled;
32320
- // Map overlay excludes to effective deny-list globs for the engine:
32321
- // - subtree roots like "docs" -> "docs/**"
32322
- // - existing glob patterns (contain *, ?, or [) pass through unchanged.
32323
- const overlayExcludesRaw = shouldMap ? overlay.excludesOverlay : [];
32324
- const overlayExcludes = overlayExcludesRaw.map(ensureSubtreeGlob);
32325
- // Also include leaf-glob excludes from inactive facets (e.g., "**/*.test.ts").
32308
+ // Overlay structural excludes are already engine-ready patterns (subtree globs and/or exact paths).
32309
+ const overlayExcludes = shouldMap ? overlay.excludesOverlay : [];
32310
+ // Also include leaf‑glob excludes from inactive facets (e.g., "**/*.test.ts").
32326
32311
  // Read facet.meta.json directly and derive leaf-globs for facets that are
32327
32312
  // currently inactive per overlay.effective.
32328
32313
  const leafGlobs = [];
@@ -32350,7 +32335,10 @@ const buildOverlayInputs = async (args) => {
32350
32335
  catch {
32351
32336
  /* best-effort only */
32352
32337
  }
32353
- const engineExcludes = Array.from(new Set([...overlayExcludes, ...leafGlobs]));
32338
+ const engineExcludes = Array.from(new Set([
32339
+ ...overlayExcludes.map(toPosix),
32340
+ ...leafGlobs.map(toPosix),
32341
+ ]));
32354
32342
  // Facet view lines for plan (same as before; keep compact)
32355
32343
  const overlayPlan = (() => {
32356
32344
  const lines = [];
package/package.json CHANGED
@@ -164,5 +164,5 @@
164
164
  },
165
165
  "type": "module",
166
166
  "types": "dist/types/index.d.ts",
167
- "version": "0.12.1"
167
+ "version": "0.12.2"
168
168
  }