You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> The server is undergoing a refactor, and this may not be required for your use case. The refactor includes allow for more simply running single simulators so this package will be primarily useful as a control plane for cases where there are many simulators under test and in use. For the previous iterations, see the `v0` branch which contain the previous functionality.
9
+
10
+
## Operation-based service orchestration
11
+
12
+
`@simulacrum/server` provides operations to start and manage services with lifecycle hooks. The recommended pattern is to create `Operation<void>` instances for each service (typically via `useService`) and pass them to `useServiceGraph` which starts the services respecting a dependency DAG and provides lifecycle hooks for startup and shutdown.
13
+
14
+
Key points:
15
+
16
+
-`useServiceGraph(services: ServicesMap): Operation<void>` — starts a DAG of services. Each service in the map is declared as a `ServiceDefinition`.
17
+
-`ServiceDefinition.operation` (required) — an `Operation<void>` which indicates the service has started. This operation may be long-lived (e.g. `useService`) or may return once the service is ready while a background child keeps the service running. See the example below.
18
+
-`deps` — an optional list of service names this service depends on; services without dependencies in the same layer are started concurrently.
19
+
- Lifecycle hooks: `beforeStart`, `afterStart`, `beforeStop`, `afterStop` — each is an `Operation<void>` that runs at the appropriate time.
// In many situations, pass `useService` directly: it returns once the
30
+
// process is spawned and, if a wellnessCheck is provided, once the
31
+
// wellnessCheck passes. The service is automatically shut down by
32
+
// effection when the operation goes out of scope.
33
+
yield*useServiceGraph({
34
+
A: {
35
+
operation: useService(
36
+
"A",
37
+
"node --import tsx ./test/services/service-a.ts"
38
+
),
39
+
},
40
+
B: {
41
+
operation: useService(
42
+
"B",
43
+
"node --import tsx ./test/services/service-b.ts"
44
+
),
45
+
deps: ["A"],
46
+
},
47
+
});
48
+
});
49
+
});
50
+
```
51
+
52
+
Notes:
53
+
54
+
-`useServiceGraph` returns an `Operation<void>` that holds while services run and only cancels on parent scope termination.
55
+
- If you want to start services sequentially or add more advanced concurrency control, compose operations yourself and use `spawn` to control how operations run.
56
+
57
+
### Lifecycle hooks
58
+
59
+
Each `ServiceDefinition` supports lifecycle hook operations. These hooks run in the parent scope and are useful for performing orchestration tasks, logging, or writing sentinel files for integration tests. Hooks are `Operation<void>` as well.
60
+
61
+
```ts
62
+
const services = {
63
+
A: {
64
+
operation: useService(
65
+
"A",
66
+
"node --import tsx ./test/services/service-a.ts"
67
+
),
68
+
afterStart: () =>
69
+
(function* () {
70
+
// runs after the operation returns
71
+
console.log("A has started");
72
+
})(),
73
+
beforeStop: () =>
74
+
(function* () {
75
+
// runs during shutdown in reverse order
76
+
console.log("A is stopping");
77
+
})(),
78
+
},
79
+
};
80
+
```
81
+
82
+
Notes:
83
+
84
+
-`afterStart` runs after `operation` returns (service is ready)
85
+
-`beforeStop` runs during cleanup in reverse-order of startup
86
+
- Hooks are optional and can be used together with a passed `operation` or a custom operation
87
+
88
+
Try it
89
+
90
+
```bash
91
+
# Run the server package tests
92
+
cd packages/server
93
+
npm test
94
+
```
95
+
96
+
## Examples
97
+
98
+
The `example` folder contains runnable examples demonstrating `useServiceGraph` and `useService`.
99
+
100
+
Run the basic dependency example:
101
+
102
+
```bash
103
+
cd packages/server
104
+
npm run example:basic
105
+
```
106
+
107
+
Run lifecycle hooks example:
108
+
109
+
```bash
110
+
cd packages/server
111
+
npm run example:lifecycle
112
+
```
113
+
114
+
Run concurrency layers example:
115
+
116
+
````bash
117
+
cd packages/server
118
+
npm run example:concurrency
119
+
120
+
Run examples directly (each example has its own npm script). You can also run the TypeScript module with `tsx`.
121
+
122
+
```bash
123
+
cd packages/server
124
+
npm run example:basic
125
+
npm run example:lifecycle
126
+
npm run example:concurrency
127
+
# or run a module directly:
128
+
node --import tsx ./example/basic-graph.ts
129
+
```
130
+
131
+
### Typed exports between services 💡
132
+
133
+
Services may return a value from their `operation`. That value is exposed to dependent services via an `exportsOperation` on the provider.
This folder contains runnable examples demonstrating `useServiceGraph` and `useService`.
4
+
5
+
There are two sets of examples:
6
+
7
+
-**use-service** (top-level files like `basic-graph.ts`, `lifecycle-hooks.ts`, `concurrency-layers.ts`) — these spawn separate processes using `useService` (e.g. `node --import tsx ./example/services/*.ts`). Use these to exercise the process-based behavior.
8
+
9
+
-**operation** (under `operation/`) — these use the `httpServer()` operation directly and run entirely in-process. They are faster and more deterministic for tests and quick iteration.
0 commit comments