CodeSampleX

샘플

fast-glob 3.3.3: Prove fast-glob 3.3.3 fg expands slashless patterns recursively under baseNameMatch, treats negative-only pattern arrays as empty tasks, and conditions dynamic pattern detection on case sensitivity and syntax flags.

검증된 샘플 — npm fast-glob 3.3.3: Prove fast-glob 3.3.3 fg expands slashless patterns recursively under baseNameMatch, treats negative-only pattern arrays as…

sha256:433cc5706b3ec70f6d20f5f47d1ce361014f3b014168a2468af26b2fcb219513

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
2
빌드한 서명 키
2
선언된 환경 node linux x64 node node npm

검증 실행 환경

환경 컨트랙트 단계 실행일
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18

케이스

HOW
목표
Prove fast-glob 3.3.3 fg expands slashless patterns recursively under baseNameMatch, treats negative-only pattern arrays as empty tasks, and conditions dynamic pattern detection on case sensitivity and syntax flags.
패키지
심벌
  • fg
환경
node
생성일
2026-08-18T00:17:49Z

컨트랙트

  1. fg with baseNameMatch expands slashless patterns like *.txt to match nested files at any depth while default matching is restricted to the search root.
  2. fg with only negative patterns returns an empty array and fg.generateTasks yields zero tasks without at least one positive pattern.
  3. fg.isDynamicPattern classifies static paths as dynamic when caseSensitiveMatch is false, and respects braceExpansion and extglob disable flags.
  4. fg.posix.escapePath and fg.escapePath escape brackets so literal directory paths with glob characters match their exact files.
  5. fg.generateTasks partitions positive patterns into distinct static and dynamic tasks grouped by base directory.

파일

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
# fast-glob (fg) Behavioral Contract Notes

## Upstream Search Result

Calling `search_known_solution` for `pkg:npm/fast-glob@3.3.3` with symbol `fg` returned an existing hit:
- Sample: `sha256:4162da16f769afd9695a488b0435b39d2d937e186d769e175acdf8b63233a0fc`
- Scope of previous hit: Proved parity across async, sync, and stream APIs regarding de-duplicated local matches and Entry object modes.

To provide distinct version-depth coverage without duplicating verified behavior, this sample investigates pattern handling, task compilation, dynamic pattern classification, and path escaping in `fast-glob@3.3.3`.

## Exact Release Pinned

- Package: `fast-glob`
- Release: `3.3.3`
- Manifest: `package.json` (`"fast-glob": "3.3.3"`)
- Lockfile: `package-lock.json` (`"version": "3.3.3"`)

## Observed Behaviors and Trap Analysis

### 1. `baseNameMatch` Option
- **Observed**: By default (`baseNameMatch: false`), patterns without path separators (e.g. `*.txt`) only match files directly located at the search root (`cwd`). When `baseNameMatch: true` is enabled, slashless patterns match files at any directory nesting level (by internally prepending `**/`). Patterns that already contain a slash (e.g. `nested/*.txt`) are not modified.
- **Trap / Failure Mode**: A caller expecting `*.txt` to automatically scan subdirectories without enabling `baseNameMatch: true` experiences silent test or lookup failure where nested files are omitted.

### 2. Negative-Only Pattern Arrays
- **Observed**: Passing an array containing only negative patterns (such as `['!*.txt']`) causes `fg.sync` and `fg` to return an empty array (`[]`). Internally, `fg.generateTasks` returns an empty array of tasks (`[]`). Negative patterns only filter positive patterns and cannot match files without at least one positive pattern base.
- **Trap / Failure Mode**: Expecting `['!*.txt']` to act as "match everything except `.txt` files" silently produces zero results.

### 3. Dynamic Pattern Detection (`fg.isDynamicPattern`)
- **Observed**: `fg.isDynamicPattern` returns `false` for static paths like `nested/file.txt`. However, when `caseSensitiveMatch: false` is supplied, `fg.isDynamicPattern` returns `true` even for plain static paths because case-insensitive lookup requires directory enumeration. Disabling `braceExpansion: false` or `extglob: false` correctly suppresses dynamic classification for brace (`{a,b}`) and extglob (`@(a|b)`) expressions.

### 4. Special Character Escaping (`fg.escapePath` / `fg.posix.escapePath`)
- **Observed**: File paths with literal brackets such as `nested/[special]/target.txt` are parsed as glob character sets unless escaped. Using `fg.posix.escapePath` escapes brackets into `nested/\\[special\\]/target.txt`, ensuring literal match targeting.

### 5. Task Partitioning (`fg.generateTasks`)
- **Observed**: `fg.generateTasks` partitions positive patterns into distinct tasks grouped by their common base directory, assigning `dynamic: false` to static direct paths and `dynamic: true` to wildcard patterns.
csx.json
{"case":{"believed":"A slashless pattern like *.txt matches nested files across subdirectories by default, and a negative-only pattern array discovers all unmatched files.","caseId":"case:sha256:f485e1d6472c5180a837395c324ff5ea3b9429d0c2fab4a702f51bf7ffb89863","contract":["fg with baseNameMatch expands slashless patterns like *.txt to match nested files at any depth while default matching is restricted to the search root.","fg with only negative patterns returns an empty array and fg.generateTasks yields zero tasks without at least one positive pattern.","fg.isDynamicPattern classifies static paths as dynamic when caseSensitiveMatch is false, and respects braceExpansion and extglob disable flags.","fg.posix.escapePath and fg.escapePath escape brackets so literal directory paths with glob characters match their exact files.","fg.generateTasks partitions positive patterns into distinct static and dynamic tasks grouped by base directory."],"goal":"Prove fast-glob 3.3.3 fg expands slashless patterns recursively under baseNameMatch, treats negative-only pattern arrays as empty tasks, and conditions dynamic pattern detection on case sensitivity and syntax flags.","kind":"HOW","packages":["pkg:npm/fast-glob@3.3.3"],"schemaVersion":1,"symbols":["fg"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/fast-glob@3.3.3"],"schemaVersion":1,"symbols":["fg"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "fast-glob-behavior-contract",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "fast-glob-behavior-contract",
      "version": "1.0.0",
      "dependencies": {
        "fast-glob": "3.3.3"
      }
    },
    "node_modules/@nodelib/fs.scandir": {
      "version": "2.1.5",
      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
      "license": "MIT",
      "dependencies": {
        "@nodelib/fs.stat": "2.0.5",
        "run-parallel": "^1.1.9"
      },
      "engines": {
        "node": ">= 8"
      }
    },
    "node_modules/@nodelib/fs.stat": {
      "version": "2.0.5",
      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
      "license": "MIT",
      "engines": {
        "node": ">= 8"
      }
    },
    "node_modules/@nodelib/fs.walk": {
      "version": "1.2.8",
      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
      "license": "MIT",
      "dependencies": {
        "@nodelib/fs.scandir": "2.1.5",
        "fastq": "^1.6.0"
      },
      "engines": {
        "node": ">= 8"
      }
    },
    "node_modules/braces": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
      "license": "MIT",
      "dependencies": {
        "fill-range": "^7.1.1"
      },
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/fast-glob": {
      "version": "3.3.3",
      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
      "license": "MIT",
      "dependencies": {
        "@nodelib/fs.stat": "^2.0.2",
        "@nodelib/fs.walk": "^1.2.3",
        "glob-parent": "^5.1.2",
        "merge2": "^1.3.0",
        "micromatch": "^4.0.8"
      },
      "engines": {
        "node": ">=8.6.0"
      }
    },
    "node_modules/fastq": {
      "version": "1.20.1",
      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
      "license": "ISC",
      "dependencies": {
        "reusify": "^1.0.4"
      }
    },
    "node_modules/fill-range": {
      "version": "7.1.1",
      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
      "license": "MIT",
      "dependencies": {
        "to-regex-range": "^5.0.1"
      },
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/glob-parent": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
      "license": "ISC",
      "dependencies": {
        "is-glob": "^4.0.1"
      },
      "engines": {
        "node": ">= 6"
      }
    },
    "node_modules/is-extglob": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
      "license": "MIT",
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/is-glob": {
      "version": "4.0.3",
      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
      "license": "MIT",
      "dependencies": {
        "is-extglob": "^2.1.1"
      },
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/is-number": {
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
      "license": "MIT",
      "engines": {
        "node": ">=0.12.0"
      }
    },
    "node_modules/merge2": {
      "version": "1.4.1",
      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
      "license": "MIT",
      "engines": {
        "node": ">= 8"
      }
    },
    "node_modules/micromatch": {
      "version": "4.0.8",
      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
      "license": "MIT",
      "dependencies": {
        "braces": "^3.0.3",
        "picomatch": "^2.3.1"
      },
      "engines": {
        "node": ">=8.6"
      }
    },
    "node_modules/picomatch": {
      "version": "2.3.2",
      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
      "license": "MIT",
      "engines": {
        "node": ">=8.6"
      },
      "funding": {
        "url": "https://github.com/sponsors/jonschlinkert"
      }
    },
    "node_modules/queue-microtask": {
      "version": "1.2.3",
      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/feross"
        },
        {
          "type": "patreon",
          "url": "https://www.patreon.com/feross"
        },
        {
          "type": "consulting",
          "url": "https://feross.org/support"
        }
      ],
      "license": "MIT"
    },
    "node_modules/reusify": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
      "license": "MIT",
      "engines": {
        "iojs": ">=1.0.0",
        "node": ">=0.10.0"
      }
    },
    "node_modules/run-parallel": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/feross"
        },
        {
          "type": "patreon",
          "url": "https://www.patreon.com/feross"
        },
        {
          "type": "consulting",
          "url": "https://feross.org/support"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "queue-microtask": "^1.2.2"
      }
    },
    "node_modules/to-regex-range": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
      "license": "MIT",
      "dependencies": {
        "is-number": "^7.0.0"
      },
      "engines": {
        "node": ">=8.0"
      }
    }
  }
}
package.json
{
  "name": "fast-glob-behavior-contract",
  "version": "1.0.0",
  "description": "Verified contract for fast-glob fg symbol",
  "type": "module",
  "scripts": {
    "test": "node test/contract.mjs"
  },
  "dependencies": {
    "fast-glob": "3.3.3"
  }
}
test/contract.mjs
import assert from 'node:assert';
import fs from 'node:fs';
import path from 'node:path';
import fg from 'fast-glob';

const fixtureDir = path.join('test_fixture');

function setupFixture() {
  fs.rmSync(fixtureDir, { recursive: true, force: true });
  fs.mkdirSync(path.join(fixtureDir, 'nested', 'deep'), { recursive: true });
  fs.mkdirSync(path.join(fixtureDir, 'nested', '[special]'), { recursive: true });

  fs.writeFileSync(path.join(fixtureDir, 'root.txt'), 'root file');
  fs.writeFileSync(path.join(fixtureDir, 'nested', 'nested.txt'), 'nested file');
  fs.writeFileSync(path.join(fixtureDir, 'nested', 'deep', 'deep.txt'), 'deep file');
  fs.writeFileSync(path.join(fixtureDir, 'nested', '[special]', 'target.txt'), 'special file');
  fs.writeFileSync(path.join(fixtureDir, 'other.log'), 'log file');
}

function cleanupFixture() {
  fs.rmSync(fixtureDir, { recursive: true, force: true });
}

setupFixture();

try {
  // Contract line 1: fg with baseNameMatch expands slashless patterns like *.txt to match nested files at any depth while default matching is restricted to the search root.
  const defaultSync = fg.sync('*.txt', { cwd: fixtureDir });
  const defaultAsync = await fg('*.txt', { cwd: fixtureDir });
  assert.deepStrictEqual(defaultSync, ['root.txt']);
  assert.deepStrictEqual(defaultAsync, ['root.txt']);

  const baseNameSync = fg.sync('*.txt', { cwd: fixtureDir, baseNameMatch: true }).sort();
  const baseNameAsync = (await fg('*.txt', { cwd: fixtureDir, baseNameMatch: true })).sort();
  const expectedBaseNameMatches = [
    'nested/[special]/target.txt',
    'nested/deep/deep.txt',
    'nested/nested.txt',
    'root.txt'
  ];
  assert.deepStrictEqual(baseNameSync, expectedBaseNameMatches);
  assert.deepStrictEqual(baseNameAsync, expectedBaseNameMatches);

  // Pattern containing slashes is not modified by baseNameMatch
  const slashPatternMatches = fg.sync('nested/*.txt', { cwd: fixtureDir, baseNameMatch: true });
  assert.deepStrictEqual(slashPatternMatches, ['nested/nested.txt']);

  // Contract line 2: fg with only negative patterns returns an empty array and fg.generateTasks yields zero tasks without at least one positive pattern.
  const negativeOnlyMatches = fg.sync(['!*.txt'], { cwd: fixtureDir });
  const negativeOnlyAsync = await fg(['!*.txt'], { cwd: fixtureDir });
  assert.deepStrictEqual(negativeOnlyMatches, []);
  assert.deepStrictEqual(negativeOnlyAsync, []);
  assert.deepStrictEqual(fg.generateTasks(['!*.txt']), []);

  const positiveAndNegative = fg.sync(['**/*.txt', '!nested/deep/**'], { cwd: fixtureDir }).sort();
  assert.deepStrictEqual(positiveAndNegative, [
    'nested/[special]/target.txt',
    'nested/nested.txt',
    'root.txt'
  ]);

  // Contract line 3: fg.isDynamicPattern classifies static paths as dynamic when caseSensitiveMatch is false, and respects braceExpansion and extglob disable flags.
  assert.strictEqual(fg.isDynamicPattern('nested/file.txt'), false);
  assert.strictEqual(fg.isDynamicPattern('nested/file.txt', { caseSensitiveMatch: false }), true);
  assert.strictEqual(fg.isDynamicPattern('nested/{a,b}.txt'), true);
  assert.strictEqual(fg.isDynamicPattern('nested/{a,b}.txt', { braceExpansion: false }), false);
  assert.strictEqual(fg.isDynamicPattern('nested/@(a|b).txt'), true);
  assert.strictEqual(fg.isDynamicPattern('nested/@(a|b).txt', { extglob: false }), false);

  // Contract line 4: fg.posix.escapePath and fg.escapePath escape brackets so literal directory paths with glob characters match their exact files.
  const rawPath = 'nested/[special]/target.txt';
  const escapedPosix = fg.posix.escapePath(rawPath);
  assert.strictEqual(escapedPosix, 'nested/\\[special\\]/target.txt');

  const matchedEscaped = fg.sync(escapedPosix, { cwd: fixtureDir });
  assert.deepStrictEqual(matchedEscaped, ['nested/[special]/target.txt']);

  // Contract line 5: fg.generateTasks partitions positive patterns into distinct static and dynamic tasks grouped by base directory.
  const tasks = fg.generateTasks(['src/index.ts', 'src/**/*.ts', 'lib/**/*.ts', '!**/*.d.ts']);
  assert.strictEqual(tasks.length, 3);

  assert.strictEqual(tasks[0].dynamic, false);
  assert.strictEqual(tasks[0].base, 'src');
  assert.deepStrictEqual(tasks[0].positive, ['src/index.ts']);
  assert.deepStrictEqual(tasks[0].negative, ['**/*.d.ts']);

  assert.strictEqual(tasks[1].dynamic, true);
  assert.strictEqual(tasks[1].base, 'src');
  assert.deepStrictEqual(tasks[1].positive, ['src/**/*.ts']);
  assert.deepStrictEqual(tasks[1].negative, ['**/*.d.ts']);

  assert.strictEqual(tasks[2].dynamic, true);
  assert.strictEqual(tasks[2].base, 'lib');
  assert.deepStrictEqual(tasks[2].positive, ['lib/**/*.ts']);
  assert.deepStrictEqual(tasks[2].negative, ['**/*.d.ts']);
} finally {
  cleanupFixture();
}

오리진 시더

csx-seed