@heyhuynhgiabuu/pi-pretty 0.4.0 → 0.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyhuynhgiabuu/pi-pretty",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Pretty terminal output for pi — syntax-highlighted file reads, colored bash output, tree-view directory listings, and more.",
5
5
  "author": "huynhgiabuu",
6
6
  "license": "MIT",
@@ -0,0 +1,28 @@
1
+ # pi-pretty v0.4.1
2
+
3
+ ## Summary
4
+ This patch release prevents `grep` from aborting Pi when FFF 0.5.2 indexes Unicode filenames and the grep call includes a `path` or `glob` constraint.
5
+
6
+ ## What changed
7
+ - Constrained `grep` calls now bypass native FFF and use the SDK fallback path.
8
+ - Unconstrained `grep` still uses FFF for the fast path.
9
+ - Added regression tests for both `path` and `glob` constrained grep bypass behavior.
10
+
11
+ ## Why
12
+ `@ff-labs/fff-node@0.5.2` can panic inside native Rust constraint matching when a byte-based slice lands inside a multi-byte UTF-8 character in an indexed filename, such as an en dash (`–`). Because that panic happens across FFI, JavaScript cannot catch it; the whole `pi` process aborts.
13
+
14
+ This release avoids the crash-prone native constraint path until upstream FFF ships the Unicode boundary fix.
15
+
16
+ ## Files
17
+ - `src/index.ts`
18
+ - `test/fff-integration.test.ts`
19
+ - `package.json`
20
+ - `package-lock.json`
21
+
22
+ ## Verification
23
+ - `npm run typecheck` ✅
24
+ - `npm run lint` ✅
25
+ - `npm test` ✅ (48 tests)
26
+
27
+ ## Upgrade notes
28
+ No configuration changes required. After updating, file- or glob-scoped `grep` calls will prefer the SDK fallback for stability; broad unconstrained grep remains FFF-backed.
package/src/index.ts CHANGED
@@ -1516,13 +1516,13 @@ export default function piPrettyExtension(pi: PiPrettyApi, deps?: PiPrettyDeps):
1516
1516
  upd: unknown,
1517
1517
  ctx: ExtensionContext,
1518
1518
  ) {
1519
- // Try FFF first (SIMD-accelerated, frecency-ranked)
1520
- if (_fffFinder && !_fffFinder.isDestroyed) {
1519
+ // Try FFF first (SIMD-accelerated, frecency-ranked).
1520
+ // FFF 0.5.2 can abort the process when path/glob constraints meet
1521
+ // Unicode filenames, so constrained searches use the SDK fallback.
1522
+ if (_fffFinder && !_fffFinder.isDestroyed && !params.path && !params.glob) {
1521
1523
  try {
1522
1524
  const effectiveLimit = Math.max(1, params.limit ?? 100);
1523
- let query = params.pattern;
1524
- if (params.glob) query = `${params.glob} ${query}`;
1525
- else if (params.path) query = `${params.path} ${query}`;
1525
+ const query = params.pattern;
1526
1526
 
1527
1527
  const grepResult = _fffFinder.grep(query, {
1528
1528
  mode: params.literal ? "plain" : "regex",
@@ -370,11 +370,20 @@ describe("piPrettyExtension integration", () => {
370
370
  expect(grep).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ mode: "regex" }));
371
371
  });
372
372
 
373
- it("glob prepended to query", async () => {
373
+ it("glob constraints bypass FFF to avoid native Unicode path panic", async () => {
374
374
  const grep = vi.fn().mockReturnValue({ ok: true, value: { items: [], totalMatched: 0, nextCursor: null } });
375
375
  await loadWithFFF({ grep });
376
376
  await tools.get("grep")!.execute("t1", { pattern: "TODO", glob: "*.ts" }, null, null, {});
377
- expect(grep).toHaveBeenCalledWith("*.ts TODO", expect.any(Object));
377
+ expect(grep).not.toHaveBeenCalled();
378
+ expect(grepExec).toHaveBeenCalledOnce();
379
+ });
380
+
381
+ it("path constraints bypass FFF to avoid native Unicode path panic", async () => {
382
+ const grep = vi.fn().mockReturnValue({ ok: true, value: { items: [], totalMatched: 0, nextCursor: null } });
383
+ await loadWithFFF({ grep });
384
+ await tools.get("grep")!.execute("t1", { pattern: "TODO", path: "file_reviewapp/static/app.js" }, null, null, {});
385
+ expect(grep).not.toHaveBeenCalled();
386
+ expect(grepExec).toHaveBeenCalledOnce();
378
387
  });
379
388
 
380
389
  it("falls back to SDK on throw", async () => {