@solaqua/gji 0.12.3 → 0.12.5
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/dist/gji-bundle.mjs +43 -11
- package/dist/uv-validation.js +24 -2
- package/dist/worktree-picker.js +25 -9
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-doctor.1 +1 -1
- package/man/man1/gji-done.1 +1 -1
- package/man/man1/gji-go.1 +1 -1
- package/man/man1/gji-history.1 +1 -1
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +1 -1
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +1 -1
- package/man/man1/gji-remove.1 +1 -1
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-run-hook.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync-files.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-task.1 +1 -1
- package/man/man1/gji-undo.1 +1 -1
- package/man/man1/gji-warp.1 +1 -1
- package/man/man1/gji.1 +1 -1
- package/package.json +1 -1
package/dist/gji-bundle.mjs
CHANGED
|
@@ -15411,7 +15411,7 @@ function isWideCodePoint(codePoint) {
|
|
|
15411
15411
|
var MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY = 4;
|
|
15412
15412
|
var MAX_TASK_READ_CONCURRENCY = 8;
|
|
15413
15413
|
async function buildWorktreePromptEntries(sources, dependencies = {}) {
|
|
15414
|
-
const loading = stdin.isTTY === true && stdout.isTTY === true ? Y2({ indicator: "timer" }) : null;
|
|
15414
|
+
const loading = stdin.isTTY === true && stdout.isTTY === true && stdin.isRaw !== true ? Y2({ indicator: "timer" }) : null;
|
|
15415
15415
|
loading?.start("Loading worktrees");
|
|
15416
15416
|
try {
|
|
15417
15417
|
return await buildWorktreePromptEntriesWithoutLoading(
|
|
@@ -15631,6 +15631,7 @@ var SearchablePrompt = class {
|
|
|
15631
15631
|
selected = /* @__PURE__ */ new Set();
|
|
15632
15632
|
scope;
|
|
15633
15633
|
scopeTogglePending = false;
|
|
15634
|
+
promptClosed = false;
|
|
15634
15635
|
prompt;
|
|
15635
15636
|
run() {
|
|
15636
15637
|
return this.prompt.prompt();
|
|
@@ -15645,6 +15646,7 @@ var SearchablePrompt = class {
|
|
|
15645
15646
|
this.cancel(prompt);
|
|
15646
15647
|
return;
|
|
15647
15648
|
}
|
|
15649
|
+
if (this.scopeTogglePending) return;
|
|
15648
15650
|
if (action === "escape") {
|
|
15649
15651
|
this.handleEscapeKey(prompt);
|
|
15650
15652
|
return;
|
|
@@ -15657,12 +15659,12 @@ var SearchablePrompt = class {
|
|
|
15657
15659
|
void this.toggleScope(prompt);
|
|
15658
15660
|
return;
|
|
15659
15661
|
}
|
|
15660
|
-
if (this.searchActive && this.handleSearchKey(character, key
|
|
15662
|
+
if (this.searchActive && this.handleSearchKey(character, key)) {
|
|
15661
15663
|
this.syncValue();
|
|
15662
15664
|
renderPrompt(prompt);
|
|
15663
15665
|
return;
|
|
15664
15666
|
}
|
|
15665
|
-
if (character
|
|
15667
|
+
if (isSearchToggleKey(character, key) && !this.searchActive) {
|
|
15666
15668
|
this.searchActive = true;
|
|
15667
15669
|
this.query = "";
|
|
15668
15670
|
this.cursor = this.firstSelectableIndex();
|
|
@@ -15681,6 +15683,7 @@ var SearchablePrompt = class {
|
|
|
15681
15683
|
renderPrompt(prompt);
|
|
15682
15684
|
try {
|
|
15683
15685
|
const nextScope = await currentScope.toggle();
|
|
15686
|
+
if (this.promptClosed) return;
|
|
15684
15687
|
this.entries = nextScope.entries.map(buildSearchableWorktreeEntry);
|
|
15685
15688
|
this.scope = {
|
|
15686
15689
|
label: nextScope.label,
|
|
@@ -15691,11 +15694,12 @@ var SearchablePrompt = class {
|
|
|
15691
15694
|
this.cursor = this.firstSelectableIndex();
|
|
15692
15695
|
this.syncValue();
|
|
15693
15696
|
} catch (error) {
|
|
15697
|
+
if (this.promptClosed) return;
|
|
15694
15698
|
prompt.error = error instanceof Error ? error.message : "Could not load worktrees";
|
|
15695
15699
|
prompt.state = "error";
|
|
15696
15700
|
} finally {
|
|
15697
15701
|
this.scopeTogglePending = false;
|
|
15698
|
-
renderPrompt(prompt);
|
|
15702
|
+
if (!this.promptClosed) renderPrompt(prompt);
|
|
15699
15703
|
}
|
|
15700
15704
|
}
|
|
15701
15705
|
handleEscapeKey(prompt) {
|
|
@@ -15710,12 +15714,15 @@ var SearchablePrompt = class {
|
|
|
15710
15714
|
this.cancel(prompt);
|
|
15711
15715
|
}
|
|
15712
15716
|
cancel(prompt) {
|
|
15717
|
+
this.promptClosed = true;
|
|
15713
15718
|
prompt.state = "cancel";
|
|
15714
15719
|
renderPrompt(prompt);
|
|
15715
15720
|
closePrompt(prompt);
|
|
15716
15721
|
}
|
|
15717
|
-
handleSearchKey(character,
|
|
15718
|
-
|
|
15722
|
+
handleSearchKey(character, key) {
|
|
15723
|
+
const keyName = key?.name;
|
|
15724
|
+
const input = character ?? key?.sequence;
|
|
15725
|
+
if (keyName === "space" || input === " ") {
|
|
15719
15726
|
return false;
|
|
15720
15727
|
}
|
|
15721
15728
|
if (keyName === "backspace" || keyName === "delete") {
|
|
@@ -15723,8 +15730,8 @@ var SearchablePrompt = class {
|
|
|
15723
15730
|
this.cursor = this.firstSelectableIndex();
|
|
15724
15731
|
return true;
|
|
15725
15732
|
}
|
|
15726
|
-
if (isPrintableSearchCharacter(
|
|
15727
|
-
this.query +=
|
|
15733
|
+
if (isPrintableSearchCharacter(input)) {
|
|
15734
|
+
this.query += input;
|
|
15728
15735
|
this.cursor = this.firstSelectableIndex();
|
|
15729
15736
|
return true;
|
|
15730
15737
|
}
|
|
@@ -15769,6 +15776,7 @@ var SearchablePrompt = class {
|
|
|
15769
15776
|
submit(prompt) {
|
|
15770
15777
|
const entry = this.visibleEntries()[this.cursor];
|
|
15771
15778
|
if (!this.options.multiple) {
|
|
15779
|
+
this.promptClosed = true;
|
|
15772
15780
|
prompt.value = isSelectableEntry(entry) ? entry.value : null;
|
|
15773
15781
|
prompt.state = "submit";
|
|
15774
15782
|
renderPrompt(prompt);
|
|
@@ -15782,6 +15790,7 @@ var SearchablePrompt = class {
|
|
|
15782
15790
|
return;
|
|
15783
15791
|
}
|
|
15784
15792
|
prompt.value = [...this.selected];
|
|
15793
|
+
this.promptClosed = true;
|
|
15785
15794
|
prompt.state = "submit";
|
|
15786
15795
|
renderPrompt(prompt);
|
|
15787
15796
|
closePrompt(prompt);
|
|
@@ -16020,7 +16029,10 @@ function onWorktreeKeypress(character, key) {
|
|
|
16020
16029
|
this.worktreePrompt.handleKeypress(this, character, key);
|
|
16021
16030
|
}
|
|
16022
16031
|
function isScopeToggleKey(character, key) {
|
|
16023
|
-
return key?.name === "tab" || character === " ";
|
|
16032
|
+
return key?.name === "tab" || character === " " || key?.sequence === " ";
|
|
16033
|
+
}
|
|
16034
|
+
function isSearchToggleKey(character, key) {
|
|
16035
|
+
return character === "/" || key?.sequence === "/";
|
|
16024
16036
|
}
|
|
16025
16037
|
function resolvePromptAction(character, key) {
|
|
16026
16038
|
if (key?.ctrl && key.name === "c" || character === "") {
|
|
@@ -18883,11 +18895,12 @@ import { execFile as execFile5 } from "node:child_process";
|
|
|
18883
18895
|
import { constants as constants5 } from "node:fs";
|
|
18884
18896
|
import { lstat as lstat3, open as open3, readdir, realpath as realpath2 } from "node:fs/promises";
|
|
18885
18897
|
import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute3, join as join11, resolve as resolve8, sep as sep2 } from "node:path";
|
|
18886
|
-
import { promisify as promisify6 } from "node:util";
|
|
18898
|
+
import { promisify as promisify6, TextDecoder } from "node:util";
|
|
18887
18899
|
var execFileAsync5 = promisify6(execFile5);
|
|
18888
18900
|
var UV_VALIDATION_MAX_ENTRIES = 1e4;
|
|
18889
18901
|
var UV_VALIDATION_MAX_FILE_BYTES = 1024 * 1024;
|
|
18890
18902
|
var UV_VALIDATION_MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
18903
|
+
var UV_VALIDATION_TEXT_PROBE_BYTES = 4 * 1024;
|
|
18891
18904
|
function virtualEnvironmentPrefixes(text) {
|
|
18892
18905
|
const prefixes = [];
|
|
18893
18906
|
for (const line of text.split(/\r?\n/u)) {
|
|
@@ -19052,6 +19065,14 @@ async function readBoundedUvTextFile(path9, remainingBytes) {
|
|
|
19052
19065
|
try {
|
|
19053
19066
|
const stats = await handle.stat();
|
|
19054
19067
|
if (!stats.isFile()) return void 0;
|
|
19068
|
+
const probeBytes = Math.min(UV_VALIDATION_TEXT_PROBE_BYTES, stats.size);
|
|
19069
|
+
if (probeBytes > remainingBytes) {
|
|
19070
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
19071
|
+
}
|
|
19072
|
+
const probe = await readFilePrefix(handle, probeBytes);
|
|
19073
|
+
if (!looksLikeUtf8Text(probe, probe.byteLength === stats.size)) {
|
|
19074
|
+
return { bytes: probe.byteLength };
|
|
19075
|
+
}
|
|
19055
19076
|
if (stats.size > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
19056
19077
|
throw new Error(`uv launcher exceeds the validation size limit: ${path9}`);
|
|
19057
19078
|
}
|
|
@@ -19067,11 +19088,22 @@ async function readBoundedUvTextFile(path9, remainingBytes) {
|
|
|
19067
19088
|
throw new Error("uv launchers exceed the total validation size limit");
|
|
19068
19089
|
}
|
|
19069
19090
|
const text = contents.toString("utf8");
|
|
19070
|
-
return
|
|
19091
|
+
return looksLikeUtf8Text(contents, true) ? { bytes: contents.byteLength, text } : { bytes: contents.byteLength };
|
|
19071
19092
|
} finally {
|
|
19072
19093
|
await handle.close();
|
|
19073
19094
|
}
|
|
19074
19095
|
}
|
|
19096
|
+
function looksLikeUtf8Text(contents, complete) {
|
|
19097
|
+
if (contents.includes(0)) return false;
|
|
19098
|
+
try {
|
|
19099
|
+
new TextDecoder("utf-8", { fatal: true }).decode(contents, {
|
|
19100
|
+
stream: !complete
|
|
19101
|
+
});
|
|
19102
|
+
return true;
|
|
19103
|
+
} catch {
|
|
19104
|
+
return false;
|
|
19105
|
+
}
|
|
19106
|
+
}
|
|
19075
19107
|
async function readFilePrefix(handle, maxBytes) {
|
|
19076
19108
|
const contents = Buffer.allocUnsafe(maxBytes);
|
|
19077
19109
|
let offset = 0;
|
package/dist/uv-validation.js
CHANGED
|
@@ -2,12 +2,13 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { lstat, open, readdir, realpath } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
5
|
-
import { promisify } from "node:util";
|
|
5
|
+
import { promisify, TextDecoder } from "node:util";
|
|
6
6
|
import { isNotFoundError } from "./fs-utils.js";
|
|
7
7
|
const execFileAsync = promisify(execFile);
|
|
8
8
|
const UV_VALIDATION_MAX_ENTRIES = 10_000;
|
|
9
9
|
const UV_VALIDATION_MAX_FILE_BYTES = 1024 * 1024;
|
|
10
10
|
const UV_VALIDATION_MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
11
|
+
const UV_VALIDATION_TEXT_PROBE_BYTES = 4 * 1024;
|
|
11
12
|
function virtualEnvironmentPrefixes(text) {
|
|
12
13
|
const prefixes = [];
|
|
13
14
|
for (const line of text.split(/\r?\n/u)) {
|
|
@@ -168,6 +169,14 @@ async function readBoundedUvTextFile(path, remainingBytes) {
|
|
|
168
169
|
const stats = await handle.stat();
|
|
169
170
|
if (!stats.isFile())
|
|
170
171
|
return undefined;
|
|
172
|
+
const probeBytes = Math.min(UV_VALIDATION_TEXT_PROBE_BYTES, stats.size);
|
|
173
|
+
if (probeBytes > remainingBytes) {
|
|
174
|
+
throw new Error("uv launchers exceed the total validation size limit");
|
|
175
|
+
}
|
|
176
|
+
const probe = await readFilePrefix(handle, probeBytes);
|
|
177
|
+
if (!looksLikeUtf8Text(probe, probe.byteLength === stats.size)) {
|
|
178
|
+
return { bytes: probe.byteLength };
|
|
179
|
+
}
|
|
171
180
|
if (stats.size > UV_VALIDATION_MAX_FILE_BYTES) {
|
|
172
181
|
throw new Error(`uv launcher exceeds the validation size limit: ${path}`);
|
|
173
182
|
}
|
|
@@ -183,7 +192,7 @@ async function readBoundedUvTextFile(path, remainingBytes) {
|
|
|
183
192
|
throw new Error("uv launchers exceed the total validation size limit");
|
|
184
193
|
}
|
|
185
194
|
const text = contents.toString("utf8");
|
|
186
|
-
return
|
|
195
|
+
return looksLikeUtf8Text(contents, true)
|
|
187
196
|
? { bytes: contents.byteLength, text }
|
|
188
197
|
: { bytes: contents.byteLength };
|
|
189
198
|
}
|
|
@@ -191,6 +200,19 @@ async function readBoundedUvTextFile(path, remainingBytes) {
|
|
|
191
200
|
await handle.close();
|
|
192
201
|
}
|
|
193
202
|
}
|
|
203
|
+
function looksLikeUtf8Text(contents, complete) {
|
|
204
|
+
if (contents.includes(0))
|
|
205
|
+
return false;
|
|
206
|
+
try {
|
|
207
|
+
new TextDecoder("utf-8", { fatal: true }).decode(contents, {
|
|
208
|
+
stream: !complete,
|
|
209
|
+
});
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
194
216
|
async function readFilePrefix(handle, maxBytes) {
|
|
195
217
|
const contents = Buffer.allocUnsafe(maxBytes);
|
|
196
218
|
let offset = 0;
|
package/dist/worktree-picker.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readWorktreeInfos, } from "./worktree-info.js";
|
|
|
9
9
|
const MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY = 4;
|
|
10
10
|
const MAX_TASK_READ_CONCURRENCY = 8;
|
|
11
11
|
export async function buildWorktreePromptEntries(sources, dependencies = {}) {
|
|
12
|
-
const loading = stdin.isTTY === true && stdout.isTTY === true
|
|
12
|
+
const loading = stdin.isTTY === true && stdout.isTTY === true && stdin.isRaw !== true
|
|
13
13
|
? spinner({ indicator: "timer" })
|
|
14
14
|
: null;
|
|
15
15
|
loading?.start("Loading worktrees");
|
|
@@ -214,6 +214,7 @@ class SearchablePrompt {
|
|
|
214
214
|
selected = new Set();
|
|
215
215
|
scope;
|
|
216
216
|
scopeTogglePending = false;
|
|
217
|
+
promptClosed = false;
|
|
217
218
|
prompt;
|
|
218
219
|
constructor(options, input, output) {
|
|
219
220
|
this.options = options;
|
|
@@ -244,6 +245,8 @@ class SearchablePrompt {
|
|
|
244
245
|
this.cancel(prompt);
|
|
245
246
|
return;
|
|
246
247
|
}
|
|
248
|
+
if (this.scopeTogglePending)
|
|
249
|
+
return;
|
|
247
250
|
if (action === "escape") {
|
|
248
251
|
this.handleEscapeKey(prompt);
|
|
249
252
|
return;
|
|
@@ -256,12 +259,12 @@ class SearchablePrompt {
|
|
|
256
259
|
void this.toggleScope(prompt);
|
|
257
260
|
return;
|
|
258
261
|
}
|
|
259
|
-
if (this.searchActive && this.handleSearchKey(character, key
|
|
262
|
+
if (this.searchActive && this.handleSearchKey(character, key)) {
|
|
260
263
|
this.syncValue();
|
|
261
264
|
renderPrompt(prompt);
|
|
262
265
|
return;
|
|
263
266
|
}
|
|
264
|
-
if (character
|
|
267
|
+
if (isSearchToggleKey(character, key) && !this.searchActive) {
|
|
265
268
|
this.searchActive = true;
|
|
266
269
|
this.query = "";
|
|
267
270
|
this.cursor = this.firstSelectableIndex();
|
|
@@ -281,6 +284,8 @@ class SearchablePrompt {
|
|
|
281
284
|
renderPrompt(prompt);
|
|
282
285
|
try {
|
|
283
286
|
const nextScope = await currentScope.toggle();
|
|
287
|
+
if (this.promptClosed)
|
|
288
|
+
return;
|
|
284
289
|
this.entries = nextScope.entries.map(buildSearchableWorktreeEntry);
|
|
285
290
|
this.scope = {
|
|
286
291
|
label: nextScope.label,
|
|
@@ -292,13 +297,16 @@ class SearchablePrompt {
|
|
|
292
297
|
this.syncValue();
|
|
293
298
|
}
|
|
294
299
|
catch (error) {
|
|
300
|
+
if (this.promptClosed)
|
|
301
|
+
return;
|
|
295
302
|
prompt.error =
|
|
296
303
|
error instanceof Error ? error.message : "Could not load worktrees";
|
|
297
304
|
prompt.state = "error";
|
|
298
305
|
}
|
|
299
306
|
finally {
|
|
300
307
|
this.scopeTogglePending = false;
|
|
301
|
-
|
|
308
|
+
if (!this.promptClosed)
|
|
309
|
+
renderPrompt(prompt);
|
|
302
310
|
}
|
|
303
311
|
}
|
|
304
312
|
handleEscapeKey(prompt) {
|
|
@@ -313,12 +321,15 @@ class SearchablePrompt {
|
|
|
313
321
|
this.cancel(prompt);
|
|
314
322
|
}
|
|
315
323
|
cancel(prompt) {
|
|
324
|
+
this.promptClosed = true;
|
|
316
325
|
prompt.state = "cancel";
|
|
317
326
|
renderPrompt(prompt);
|
|
318
327
|
closePrompt(prompt);
|
|
319
328
|
}
|
|
320
|
-
handleSearchKey(character,
|
|
321
|
-
|
|
329
|
+
handleSearchKey(character, key) {
|
|
330
|
+
const keyName = key?.name;
|
|
331
|
+
const input = character ?? key?.sequence;
|
|
332
|
+
if (keyName === "space" || input === " ") {
|
|
322
333
|
return false;
|
|
323
334
|
}
|
|
324
335
|
if (keyName === "backspace" || keyName === "delete") {
|
|
@@ -326,8 +337,8 @@ class SearchablePrompt {
|
|
|
326
337
|
this.cursor = this.firstSelectableIndex();
|
|
327
338
|
return true;
|
|
328
339
|
}
|
|
329
|
-
if (isPrintableSearchCharacter(
|
|
330
|
-
this.query +=
|
|
340
|
+
if (isPrintableSearchCharacter(input)) {
|
|
341
|
+
this.query += input;
|
|
331
342
|
this.cursor = this.firstSelectableIndex();
|
|
332
343
|
return true;
|
|
333
344
|
}
|
|
@@ -373,6 +384,7 @@ class SearchablePrompt {
|
|
|
373
384
|
submit(prompt) {
|
|
374
385
|
const entry = this.visibleEntries()[this.cursor];
|
|
375
386
|
if (!this.options.multiple) {
|
|
387
|
+
this.promptClosed = true;
|
|
376
388
|
prompt.value = isSelectableEntry(entry) ? entry.value : null;
|
|
377
389
|
prompt.state = "submit";
|
|
378
390
|
renderPrompt(prompt);
|
|
@@ -386,6 +398,7 @@ class SearchablePrompt {
|
|
|
386
398
|
return;
|
|
387
399
|
}
|
|
388
400
|
prompt.value = [...this.selected];
|
|
401
|
+
this.promptClosed = true;
|
|
389
402
|
prompt.state = "submit";
|
|
390
403
|
renderPrompt(prompt);
|
|
391
404
|
closePrompt(prompt);
|
|
@@ -640,7 +653,10 @@ function onWorktreeKeypress(character, key) {
|
|
|
640
653
|
this.worktreePrompt.handleKeypress(this, character, key);
|
|
641
654
|
}
|
|
642
655
|
function isScopeToggleKey(character, key) {
|
|
643
|
-
return key?.name === "tab" || character === "\t";
|
|
656
|
+
return key?.name === "tab" || character === "\t" || key?.sequence === "\t";
|
|
657
|
+
}
|
|
658
|
+
function isSearchToggleKey(character, key) {
|
|
659
|
+
return character === "/" || key?.sequence === "/";
|
|
644
660
|
}
|
|
645
661
|
function resolvePromptAction(character, key) {
|
|
646
662
|
if ((key?.ctrl && key.name === "c") || character === "\u0003") {
|
package/man/man1/gji-back.1
CHANGED
package/man/man1/gji-clean.1
CHANGED
package/man/man1/gji-config.1
CHANGED
package/man/man1/gji-doctor.1
CHANGED
package/man/man1/gji-done.1
CHANGED
package/man/man1/gji-go.1
CHANGED
package/man/man1/gji-history.1
CHANGED
package/man/man1/gji-init.1
CHANGED
package/man/man1/gji-ls.1
CHANGED
package/man/man1/gji-new.1
CHANGED
package/man/man1/gji-open.1
CHANGED
package/man/man1/gji-pr.1
CHANGED
package/man/man1/gji-remove.1
CHANGED
package/man/man1/gji-root.1
CHANGED
package/man/man1/gji-run-hook.1
CHANGED
package/man/man1/gji-status.1
CHANGED
package/man/man1/gji-sync.1
CHANGED
package/man/man1/gji-task.1
CHANGED
package/man/man1/gji-undo.1
CHANGED
package/man/man1/gji-warp.1
CHANGED
package/man/man1/gji.1
CHANGED