Skip to content

Commit 0ba7f2d

Browse files
committed
useSimulation helper
1 parent 6645a87 commit 0ba7f2d

File tree

3 files changed

+63
-56
lines changed

3 files changed

+63
-56
lines changed

packages/server/example/operation/data-sharing.ts

Lines changed: 24 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
#!/usr/bin/env node
22
import { simulationCLI } from "../../src/cli.ts";
33
import { useServiceGraph } from "../../src/services.ts";
4-
import { spawn, suspend, until } from "effection";
4+
import { until } from "effection";
55
import { createFoundationSimulationServer } from "@simulacrum/foundation-simulator";
66
import http from "node:http";
7+
import { useSimulation } from "../../src/index.ts";
78

8-
export const createServiceASimulation = (seed: number): any =>
9+
export const createServiceASimulation = (
10+
seed: number
11+
): ReturnType<typeof createFoundationSimulationServer> =>
912
createFoundationSimulationServer({
1013
port: 0,
1114
extendRouter(router) {
1215
router.get("/info", (_req, res) => res.json({ seed, handledWith: seed }));
1316
},
1417
});
1518

16-
export const createServiceBSimulation = (used: number): any =>
19+
export const createServiceBSimulation = (
20+
used: number
21+
): ReturnType<typeof createFoundationSimulationServer> =>
1722
createFoundationSimulationServer({
1823
port: 0,
1924
extendRouter(router) {
2025
router.get("/info", (_req, res) => res.json({ used }));
2126
},
2227
});
2328

24-
export const createServiceCSimulation = (message: string): any =>
29+
export const createServiceCSimulation = (
30+
message: string
31+
): ReturnType<typeof createFoundationSimulationServer> =>
2532
createFoundationSimulationServer({
2633
port: 0,
2734
extendRouter(router) {
@@ -43,15 +50,9 @@ export const services = useServiceGraph({
4350
serviceA: {
4451
deps: ["data"],
4552
*operation({ data }) {
46-
const createSim = createServiceASimulation(data.seed)();
47-
48-
const listening: any = yield* until(createSim.listen());
49-
50-
// debug log so tests can see the assigned port (left as example output)
51-
// eslint-disable-next-line no-console
52-
console.log(
53-
`[data-sharing] started foundation sim on port ${listening.port}`
54-
);
53+
const { port: listeningPort } = yield* useSimulation(
54+
createServiceASimulation
55+
)(data.seed);
5556

5657
// self-check
5758
try {
@@ -60,7 +61,7 @@ export const services = useServiceGraph({
6061
const req = http.get(
6162
{
6263
hostname: "127.0.0.1",
63-
port: listening.port,
64+
port: listeningPort,
6465
path: "/info",
6566
agent: false,
6667
},
@@ -80,48 +81,23 @@ export const services = useServiceGraph({
8081
console.log(`[data-sharing] self-check error:`, err);
8182
}
8283

83-
// spawn background keeper which calls ensureClose when finalized
84-
yield* spawn(function* () {
85-
try {
86-
yield* suspend();
87-
} finally {
88-
// eslint-disable-next-line no-console
89-
console.log(
90-
`[data-sharing] ensuring close for port ${listening.port}`
91-
);
92-
yield* until(listening.ensureClose());
93-
// eslint-disable-next-line no-console
94-
console.log(
95-
`[data-sharing] closed foundation sim on port ${listening.port}`
96-
);
97-
}
98-
});
99-
100-
return { handledWith: data.seed, port: listening.port };
84+
return { handledWith: data.seed, port: listeningPort };
10185
},
10286
},
10387

10488
// serviceB depends on serviceA and data
10589
serviceB: {
10690
deps: ["serviceA", "data"],
10791
*operation({ serviceA, data }) {
108-
const createSim = createServiceBSimulation(serviceA.handledWith)();
109-
110-
const listening: any = yield* until(createSim.listen());
111-
112-
yield* spawn(function* () {
113-
try {
114-
yield* suspend();
115-
} finally {
116-
yield* until(listening.ensureClose());
117-
}
118-
});
92+
const { port: listeningPort } = yield* useSimulation(
93+
createServiceBSimulation
94+
)(serviceA.handledWith);
11995

12096
// include data seed in the export so tests can verify multi-dependency wiring
12197
return {
12298
used: serviceA.handledWith,
12399
dataSeed: data.seed,
124-
port: listening.port,
100+
port: listeningPort,
125101
};
126102
},
127103
},
@@ -130,19 +106,11 @@ export const services = useServiceGraph({
130106
serviceC: {
131107
deps: ["data"],
132108
*operation({ data }) {
133-
const createSim = createServiceCSimulation(data.message)();
134-
135-
const listening: any = yield* until(createSim.listen());
136-
137-
yield* spawn(function* () {
138-
try {
139-
yield* suspend();
140-
} finally {
141-
yield* until(listening.ensureClose());
142-
}
143-
});
109+
const { port: listeningPort } = yield* useSimulation(
110+
createServiceCSimulation
111+
)(data.message);
144112

145-
return { dataMessage: data.message, port: listening.port };
113+
return { dataMessage: data.message, port: listeningPort };
146114
},
147115
},
148116
});

packages/server/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./logging.ts";
22
export * from "./service.ts";
33
export * from "./services.ts";
4+
export * from "./simulation.ts";

packages/server/src/simulation.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { spawn, suspend, until } from "effection";
2+
import type { Operation } from "effection";
3+
import type {
4+
FoundationSimulator,
5+
FoundationSimulatorListening,
6+
} from "@simulacrum/foundation-simulator";
7+
8+
/**
9+
* Helper to start a foundation simulation server factory and return the listening
10+
* information in a typed way.
11+
*/
12+
export function useSimulation<A extends any[], L extends object = any>(
13+
createFactory: (...args: A) => () => FoundationSimulator<L>
14+
): (...args: A) => Operation<FoundationSimulatorListening<L>> {
15+
return function* (...args: A) {
16+
const createSim = createFactory(...args)();
17+
const listening: FoundationSimulatorListening<L> = yield* until(
18+
createSim.listen()
19+
);
20+
21+
// small debug log to make it visible in tests
22+
// eslint-disable-next-line no-console
23+
console.log(`simulation started on port ${listening.port}`);
24+
25+
// ensure server is closed when this operation is finalized
26+
yield* spawn(function* () {
27+
try {
28+
yield* suspend();
29+
} finally {
30+
yield* until(listening.ensureClose());
31+
// eslint-disable-next-line no-console
32+
console.log(`simulation closed on port ${listening.port}`);
33+
}
34+
});
35+
36+
return listening;
37+
};
38+
}

0 commit comments

Comments
 (0)