Skip to content

Commit 62c3a39

Browse files
dahliacodex
andcommitted
Preserve recurse streaming and inject lookup exit
Restore recurse-mode output timing so the root object is emitted immediately when --reverse is not used, preserving prior streaming/visibility semantics. Also make runLookup accept an injected exit dependency and update the runLookup ordering tests to use that injection instead of global process.exit monkeypatching. #609 (comment) #609 (comment) Co-Authored-By: OpenAI Codex <codex@openai.com>
1 parent 1356295 commit 62c3a39

2 files changed

Lines changed: 82 additions & 46 deletions

File tree

packages/cli/src/lookup.test.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -943,18 +943,17 @@ async function runLookupAndCaptureExitCode(
943943
command: Parameters<typeof runLookup>[0],
944944
deps?: Parameters<typeof runLookup>[1],
945945
): Promise<number | null> {
946-
const originalExit = process.exit;
947-
process.exit = ((code?: number) => {
948-
throw new ExitSignal(code ?? 0);
949-
}) as typeof process.exit;
950946
try {
951-
await runLookup(command, deps);
947+
await runLookup(command, {
948+
...deps,
949+
exit: (code: number) => {
950+
throw new ExitSignal(code);
951+
},
952+
});
952953
return null;
953954
} catch (error) {
954955
if (error instanceof ExitSignal) return error.code;
955956
throw error;
956-
} finally {
957-
process.exit = originalExit;
958957
}
959958
}
960959

packages/cli/src/lookup.ts

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -674,17 +674,26 @@ export async function collectRecursiveObjects(
674674

675675
export async function runLookup(
676676
command: InferValue<typeof lookupCommand> & GlobalOptions,
677-
deps: {
677+
deps: Partial<{
678678
lookupObject: typeof lookupObject;
679679
traverseCollection: typeof traverseCollection;
680+
exit: (code: number) => never;
681+
}> = {},
682+
) {
683+
const effectiveDeps: {
684+
lookupObject: typeof lookupObject;
685+
traverseCollection: typeof traverseCollection;
686+
exit: (code: number) => never;
680687
} = {
681688
lookupObject,
682689
traverseCollection,
683-
},
684-
) {
690+
exit: (code: number) => process.exit(code),
691+
...deps,
692+
};
693+
685694
if (command.urls.length < 1) {
686695
printError(message`At least one URL or actor handle must be provided.`);
687-
process.exit(1);
696+
effectiveDeps.exit(1);
688697
}
689698

690699
// Enable Debug mode if requested
@@ -763,7 +772,7 @@ export async function runLookup(
763772
},
764773
);
765774
}
766-
process.exit(cleanupFailed && code === 0 ? 1 : code);
775+
effectiveDeps.exit(cleanupFailed && code === 0 ? 1 : code);
767776
};
768777

769778
if (command.authorizedFetch) {
@@ -892,7 +901,7 @@ export async function runLookup(
892901
}
893902
let current: APObject | null = null;
894903
try {
895-
current = await deps.lookupObject(url, {
904+
current = await effectiveDeps.lookupObject(url, {
896905
documentLoader: initialLookupDocumentLoader,
897906
contextLoader,
898907
userAgent: command.userAgent,
@@ -923,21 +932,7 @@ export async function runLookup(
923932
visited.add(current.id.href);
924933
}
925934

926-
let chain: APObject[] = [];
927-
try {
928-
chain = await collectRecursiveObjects(
929-
current,
930-
command.recurse,
931-
recurseDepth,
932-
(target) =>
933-
deps.lookupObject(target, {
934-
documentLoader: recursiveLookupDocumentLoader,
935-
contextLoader: recursiveContextLoader,
936-
userAgent: command.userAgent,
937-
}),
938-
{ suppressErrors: command.suppressErrors, visited },
939-
);
940-
} catch (error) {
935+
if (!command.reverse) {
941936
try {
942937
if (totalObjects > 0) {
943938
await writeSeparator(command.separator, getOutputStream());
@@ -950,14 +945,51 @@ export async function runLookup(
950945
getOutputStream(),
951946
);
952947
totalObjects++;
953-
} catch (writeError) {
954-
logger.error("Failed to write lookup output: {error}", {
955-
error: writeError,
956-
});
948+
} catch (error) {
949+
logger.error("Failed to write lookup output: {error}", { error });
957950
spinner.fail("Failed to write output.");
958951
await finalizeAndExit(1);
959952
return;
960953
}
954+
}
955+
956+
let chain: APObject[] = [];
957+
try {
958+
chain = await collectRecursiveObjects(
959+
current,
960+
command.recurse,
961+
recurseDepth,
962+
(target) =>
963+
effectiveDeps.lookupObject(target, {
964+
documentLoader: recursiveLookupDocumentLoader,
965+
contextLoader: recursiveContextLoader,
966+
userAgent: command.userAgent,
967+
}),
968+
{ suppressErrors: command.suppressErrors, visited },
969+
);
970+
} catch (error) {
971+
if (command.reverse) {
972+
try {
973+
if (totalObjects > 0) {
974+
await writeSeparator(command.separator, getOutputStream());
975+
}
976+
await writeObjectToStream(
977+
current,
978+
command.output,
979+
command.format,
980+
contextLoader,
981+
getOutputStream(),
982+
);
983+
totalObjects++;
984+
} catch (writeError) {
985+
logger.error("Failed to write lookup output: {error}", {
986+
error: writeError,
987+
});
988+
spinner.fail("Failed to write output.");
989+
await finalizeAndExit(1);
990+
return;
991+
}
992+
}
961993
logger.error(
962994
"Failed to recursively fetch an object in chain: {error}",
963995
{
@@ -990,16 +1022,21 @@ export async function runLookup(
9901022
return;
9911023
}
9921024

993-
const chainEntries = toPresentationOrder(
994-
[
995-
{ object: current, objectContextLoader: contextLoader },
996-
...chain.map((next) => ({
997-
object: next,
998-
objectContextLoader: recursiveContextLoader,
999-
})),
1000-
],
1001-
command.reverse,
1002-
);
1025+
const chainEntries = command.reverse
1026+
? toPresentationOrder(
1027+
[
1028+
{ object: current, objectContextLoader: contextLoader },
1029+
...chain.map((next) => ({
1030+
object: next,
1031+
objectContextLoader: recursiveContextLoader,
1032+
})),
1033+
],
1034+
true,
1035+
)
1036+
: chain.map((next) => ({
1037+
object: next,
1038+
objectContextLoader: recursiveContextLoader,
1039+
}));
10031040
for (let chainIndex = 0; chainIndex < chainEntries.length; chainIndex++) {
10041041
const entry = chainEntries[chainIndex];
10051042
try {
@@ -1042,7 +1079,7 @@ export async function runLookup(
10421079

10431080
let collection: APObject | null = null;
10441081
try {
1045-
collection = await deps.lookupObject(url, {
1082+
collection = await effectiveDeps.lookupObject(url, {
10461083
documentLoader: authLoader ?? documentLoader,
10471084
contextLoader,
10481085
userAgent: command.userAgent,
@@ -1083,7 +1120,7 @@ export async function runLookup(
10831120
items: traversedItems,
10841121
error: traversalError,
10851122
} = await collectAsyncItems(
1086-
deps.traverseCollection(collection, {
1123+
effectiveDeps.traverseCollection(collection, {
10871124
documentLoader: authLoader ?? documentLoader,
10881125
contextLoader,
10891126
suppressError: command.suppressErrors,
@@ -1117,7 +1154,7 @@ export async function runLookup(
11171154
}
11181155
} else {
11191156
for await (
1120-
const item of deps.traverseCollection(collection, {
1157+
const item of effectiveDeps.traverseCollection(collection, {
11211158
documentLoader: authLoader ?? documentLoader,
11221159
contextLoader,
11231160
suppressError: command.suppressErrors,
@@ -1180,7 +1217,7 @@ export async function runLookup(
11801217

11811218
for (const url of command.urls) {
11821219
promises.push(
1183-
deps.lookupObject(url, {
1220+
effectiveDeps.lookupObject(url, {
11841221
documentLoader: authLoader ?? documentLoader,
11851222
contextLoader,
11861223
userAgent: command.userAgent,

0 commit comments

Comments
 (0)