Skip to content

Commit eda2e5c

Browse files
committed
fix(formats): prevent CLI execution when bundled
Add filename check to CLI guard condition in format handlers to prevent execution when code is bundled (e.g., via Vite). The previous check only verified if the module URL matched the process argument, which could match incorrectly in bundled contexts. build(vite): simplify external module pattern Replace array-based externals with function pattern for node: prefix matching. This is more concise and handles all node: modules uniformly. refactor(framework): add sync case registry variant Add registerCasesSync() function for backward compatibility with code that doesn't require async seed pre-loading. The async registerCases() now pre-loads benchmark data to determine seed nodes upfront. Update entry point to use registerCasesSync for consistency.
1 parent f17d5fa commit eda2e5c

File tree

10 files changed

+131
-62
lines changed

10 files changed

+131
-62
lines changed

src/experiments/framework/entry/run-framework-experiments.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { fileURLToPath } from "node:url";
1919

2020
import { getCoreClaims } from "../claims/registry.js";
2121
import { CaseRegistry } from "../registry/case-registry.js";
22-
import { registerCases } from "../registry/register-cases.js";
22+
import { registerCasesSync } from "../registry/register-cases.js";
2323
import { registerExpansionSuts } from "../registry/register-suts.js";
2424
import { SUTRegistry } from "../registry/sut-registry.js";
2525
import { TABLE_SPECS } from "../renderers/table-specs.js";
@@ -50,7 +50,7 @@ const main = async (): Promise<void> => {
5050
// Initialize registries
5151
console.log("1. Initializing registries...");
5252
const sutRegistry = registerExpansionSuts(new SUTRegistry());
53-
const caseRegistry = registerCases(new CaseRegistry());
53+
const caseRegistry = registerCasesSync(new CaseRegistry());
5454

5555
console.log(` - ${sutRegistry.size} SUTs registered`);
5656
console.log(` - ${caseRegistry.size} cases registered`);

src/experiments/framework/registry/register-cases.ts

Lines changed: 114 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,65 @@ export const BENCHMARK_CASES: Array<{
5252

5353
/**
5454
* Create case definitions for benchmark graphs.
55+
* Pre-loads benchmark data to determine seed nodes.
5556
*/
56-
const createBenchmarkCaseDefinitions = (): CaseDefinition<BenchmarkGraphExpander>[] => BENCHMARK_CASES.map((benchmark) => {
57-
const inputs: CaseInputs = {
58-
summary: {
59-
datasetId: benchmark.id,
60-
expectedNodes: benchmark.expectedNodes,
61-
},
62-
artefacts: [
63-
{
64-
type: "graph",
65-
uri: `benchmark://${benchmark.id}`,
57+
const createBenchmarkCaseDefinitions = async (): Promise<CaseDefinition<BenchmarkGraphExpander>[]> => {
58+
const cases: CaseDefinition<BenchmarkGraphExpander>[] = [];
59+
60+
for (const benchmark of BENCHMARK_CASES) {
61+
// Load benchmark data to get node IDs
62+
const benchmarkData = await loadBenchmarkByIdFromUrl(benchmark.id);
63+
const nodes = benchmarkData.graph.getAllNodes();
64+
65+
// Determine seed nodes (first and last)
66+
let seeds: string[];
67+
if (nodes.length >= 2) {
68+
const lastNode = nodes.at(-1);
69+
seeds = lastNode ? [nodes[0].id, lastNode.id] : [nodes[0].id];
70+
} else if (nodes.length === 1) {
71+
seeds = [nodes[0].id];
72+
} else {
73+
seeds = [];
74+
}
75+
76+
const inputs: CaseInputs = {
77+
summary: {
78+
datasetId: benchmark.id,
79+
expectedNodes: benchmark.expectedNodes,
80+
seeds, // Store seeds in inputs
6681
},
67-
],
68-
};
82+
artefacts: [
83+
{
84+
type: "graph",
85+
uri: `benchmark://${benchmark.id}`,
86+
},
87+
],
88+
};
6989

70-
const caseSpec: EvaluationCase = {
71-
caseId: generateCaseId(benchmark.name, inputs),
72-
name: benchmark.name,
73-
caseClass: benchmark.caseClass,
74-
inputs,
75-
version: "1.0.0",
76-
tags: benchmark.tags,
77-
};
90+
const caseSpec: EvaluationCase = {
91+
caseId: generateCaseId(benchmark.name, inputs),
92+
name: benchmark.name,
93+
caseClass: benchmark.caseClass,
94+
inputs,
95+
version: "1.0.0",
96+
tags: benchmark.tags,
97+
};
7898

79-
return {
80-
case: caseSpec,
81-
createExpander: async (inputsForExpander: CaseInputs) => {
82-
const datasetId = inputsForExpander.summary?.datasetId as string;
83-
const benchmarkData = await loadBenchmarkByIdFromUrl(datasetId);
84-
return new BenchmarkGraphExpander(benchmarkData.graph, benchmarkData.meta.directed);
85-
},
86-
getSeeds: (_inputsForSeeds: CaseInputs) => {
87-
// Default: return first and last nodes (will be populated after loading)
88-
// In practice, seeds are determined after the expander is created
89-
return [];
90-
},
91-
};
92-
});
99+
cases.push({
100+
case: caseSpec,
101+
createExpander: async (_inputsForExpander: CaseInputs) => {
102+
// Use the pre-loaded data
103+
return new BenchmarkGraphExpander(benchmarkData.graph, benchmarkData.meta.directed);
104+
},
105+
getSeeds: (_inputsForSeeds: CaseInputs) => {
106+
// Return the pre-computed seeds
107+
return seeds;
108+
},
109+
});
110+
}
111+
112+
return cases;
113+
};
93114

94115
/**
95116
* Synthetic graph case class types.
@@ -132,23 +153,76 @@ export const createSyntheticCaseDefinition = (type: SyntheticGraphType, nodes: n
132153
};
133154

134155
/**
135-
* Register all cases with a registry.
156+
* Register all cases with a registry (sync version, doesn't pre-load seeds).
157+
* Use this for backward compatibility when seeds aren't needed upfront.
158+
*
159+
* @param registry - Registry to populate (defaults to new instance)
160+
* @returns The populated registry
161+
*/
162+
export const registerCasesSync = (registry: GraphCaseRegistry = new CaseRegistry()): GraphCaseRegistry => {
163+
// Register benchmark cases (without seed pre-loading)
164+
const cases: CaseDefinition<BenchmarkGraphExpander>[] = BENCHMARK_CASES.map((benchmark) => {
165+
const inputs: CaseInputs = {
166+
summary: {
167+
datasetId: benchmark.id,
168+
expectedNodes: benchmark.expectedNodes,
169+
},
170+
artefacts: [
171+
{
172+
type: "graph",
173+
uri: `benchmark://${benchmark.id}`,
174+
},
175+
],
176+
};
177+
178+
const caseSpec: EvaluationCase = {
179+
caseId: generateCaseId(benchmark.name, inputs),
180+
name: benchmark.name,
181+
caseClass: benchmark.caseClass,
182+
inputs,
183+
version: "1.0.0",
184+
tags: benchmark.tags,
185+
};
186+
187+
return {
188+
case: caseSpec,
189+
createExpander: async (inputsForExpander: CaseInputs) => {
190+
const datasetId = inputsForExpander.summary?.datasetId as string;
191+
const benchmarkData = await loadBenchmarkByIdFromUrl(datasetId);
192+
return new BenchmarkGraphExpander(benchmarkData.graph, benchmarkData.meta.directed);
193+
},
194+
getSeeds: (_inputsForSeeds: CaseInputs) => {
195+
// Seeds will be determined from the expander after creation
196+
// The SUT factory should handle empty seeds
197+
return [];
198+
},
199+
};
200+
});
201+
202+
registry.registerAll(cases);
203+
return registry;
204+
};
205+
206+
/**
207+
* Register all cases with a registry (async version, pre-loads seeds).
208+
* This version loads benchmark data during registration to determine seed nodes.
136209
*
137210
* @param registry - Registry to populate (defaults to new instance)
138211
* @returns The populated registry
139212
*/
140-
export const registerCases = (registry: GraphCaseRegistry = new CaseRegistry()): GraphCaseRegistry => {
141-
// Register benchmark cases
142-
const benchmarkCases = createBenchmarkCaseDefinitions();
213+
export const registerCases = async (registry: GraphCaseRegistry = new CaseRegistry()): Promise<GraphCaseRegistry> => {
214+
// Register benchmark cases (async now, pre-loads seeds)
215+
const benchmarkCases = await createBenchmarkCaseDefinitions();
143216
registry.registerAll(benchmarkCases);
144217

145218
return registry;
146219
};
147220

148221
/**
149-
* Global case registry with all cases registered.
222+
* Global case registry with all cases registered (sync version).
223+
* For async version with pre-loaded seeds, use `await registerCases()` instead.
150224
*/
151-
export const graphCaseRegistry = registerCases(new CaseRegistry());
225+
export const graphCaseRegistry = registerCasesSync(new CaseRegistry());
152226

153227
/**
154228
* Get seeds for a benchmark graph.

src/formats/gml/fetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ const main = async (): Promise<void> => {
191191
}
192192
};
193193

194-
// Run CLI if executed directly
195-
if (import.meta.url === `file://${process.argv[1]}`) {
194+
// Run CLI if executed directly (not when bundled)
195+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("fetch.ts")) {
196196
main().catch((error: unknown) => {
197197
console.error("Error:", error);
198198
process.exit(1);

src/formats/gml/parse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,8 @@ const main = async (): Promise<void> => {
405405
}
406406
};
407407

408-
// Run CLI if executed directly
409-
if (import.meta.url === `file://${process.argv[1]}`) {
408+
// Run CLI if executed directly (not when bundled)
409+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("parse.ts")) {
410410
main().catch((error: unknown) => {
411411
console.error("Error:", error);
412412
process.exit(1);

src/formats/gml/serialize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ const main = async (): Promise<void> => {
181181
}
182182
};
183183

184-
// Run CLI if executed directly
185-
if (import.meta.url === `file://${process.argv[1]}`) {
184+
// Run CLI if executed directly (not when bundled)
185+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("serialize.ts")) {
186186
main().catch((error: unknown) => {
187187
console.error("Error:", error);
188188
process.exit(1);

src/formats/pajek/fetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ const main = async (): Promise<void> => {
174174
}
175175
};
176176

177-
// Run CLI if executed directly
178-
if (import.meta.url === `file://${process.argv[1]}`) {
177+
// Run CLI if executed directly (not when bundled)
178+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("fetch.ts")) {
179179
main().catch((error: unknown) => {
180180
console.error("Error:", error);
181181
process.exit(1);

src/formats/snap/fetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ const main = async (): Promise<void> => {
140140
}
141141
};
142142

143-
// Run CLI if executed directly
144-
if (import.meta.url === `file://${process.argv[1]}`) {
143+
// Run CLI if executed directly (not when bundled)
144+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("fetch.ts")) {
145145
main().catch((error: unknown) => {
146146
console.error("Error:", error);
147147
process.exit(1);

src/formats/snap/parse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ const main = async (): Promise<void> => {
192192
console.error(`Nodes: ${json.nodes.length}, Edges: ${json.edges.length}, Directed: ${json.meta.directed}`);
193193
};
194194

195-
// Run CLI if executed directly
196-
if (import.meta.url === `file://${process.argv[1]}`) {
195+
// Run CLI if executed directly (not when bundled)
196+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("parse.ts")) {
197197
main().catch((error: unknown) => {
198198
console.error("Error:", error);
199199
process.exit(1);

src/formats/ucinet/fetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ const main = async (): Promise<void> => {
174174
}
175175
};
176176

177-
// Run CLI if executed directly
178-
if (import.meta.url === `file://${process.argv[1]}`) {
177+
// Run CLI if executed directly (not when bundled)
178+
if (import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("fetch.ts")) {
179179
main().catch((error: unknown) => {
180180
console.error("Error:", error);
181181
process.exit(1);

vite.config.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ export default defineConfig({
3434
target: "node18",
3535
modulePreload: false,
3636
rollupOptions: {
37-
external: [
38-
"node:fs",
39-
"node:path",
40-
"node:process",
41-
"node:module",
42-
],
37+
external: (id: string) => id.startsWith("node:"),
4338
},
4439
}
4540
: {

0 commit comments

Comments
 (0)