Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1db473a
Initial plan
Copilot Apr 3, 2026
7c237fa
Pass openApiDocument to Context constructor in module-loader
Copilot Apr 3, 2026
2cfdd55
Ensure openApiDocument updates reach Context instances when spec chan…
Copilot Apr 3, 2026
4c25b46
Replace in-place key mutation with a read-only Proxy for openApiDocum…
Copilot Apr 3, 2026
a20b89c
Simplify Proxy: drop .current wrapper, default openApiDocumentRef to {}
Copilot Apr 4, 2026
939f7e1
Merge branch 'main' into copilot/pass-openapi-document-to-context
pmcelhaney Apr 4, 2026
1665e29
Merge branch 'main' into copilot/pass-openapi-document-to-context
pmcelhaney Apr 6, 2026
f9d7dc7
Resolve merge conflict markers in module-loader.ts and app.ts
Copilot Apr 6, 2026
05a3c31
Fix TS2741: make paths optional in OpenApiDocument to allow empty def…
Copilot Apr 6, 2026
d653169
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
fdffde6
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
6a62ed1
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
814ba54
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
fae0bcc
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
0c8e6ff
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
7545c07
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
4fd18fd
Merge branch 'main' into copilot/pass-openapi-document-to-context
github-actions[bot] Apr 7, 2026
04b7b69
Merge branch 'main' into copilot/pass-openapi-document-to-context
pmcelhaney Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/pass-openapi-document-to-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"counterfact": minor
---

Pass the OpenAPI document to the Context class constructor in `_.context.ts`.

The `Context` constructor now receives an `openApiDocument` property alongside the existing `loadContext` and `readJson` helpers:

```ts
// routes/_.context.ts
export class Context {
constructor({ openApiDocument, loadContext, readJson }) {
this.openApiDocument = openApiDocument;
}
}
```
6 changes: 5 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export async function createMswHandlers(
compiledPathsDirectory,
registry,
contextRegistry,
openApiDocument,
);
await moduleLoader.load();
const routes = registry.routes;
Expand Down Expand Up @@ -129,10 +130,12 @@ export async function counterfact(config: Config) {
config.generate,
);

const openApiDocument = await loadOpenApiDocument(config.openApiPath);

const dispatcher = new Dispatcher(
registry,
contextRegistry,
await loadOpenApiDocument(config.openApiPath),
openApiDocument,
config,
);

Expand All @@ -146,6 +149,7 @@ export async function counterfact(config: Config) {
compiledPathsDirectory,
registry,
contextRegistry,
openApiDocument,
);

const middleware = koaMiddleware(dispatcher, config);
Expand Down
6 changes: 6 additions & 0 deletions src/server/module-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import createDebug from "debug";
import { CHOKIDAR_OPTIONS } from "./constants.js";
import { type Context, ContextRegistry } from "./context-registry.js";
import { determineModuleKind } from "./determine-module-kind.js";
import type { OpenApiDocument } from "./dispatcher.js";
import { ModuleDependencyGraph } from "./module-dependency-graph.js";
import type { MiddlewareFunction, Module, Registry } from "./registry.js";
import { uncachedImport } from "./uncached-import.js";
Expand Down Expand Up @@ -51,6 +52,8 @@ export class ModuleLoader extends EventTarget {

private readonly contextRegistry: ContextRegistry;

private readonly openApiDocument: OpenApiDocument | undefined;

private readonly dependencyGraph = new ModuleDependencyGraph();

private readonly uncachedImport: (moduleName: string) => Promise<unknown> =
Expand All @@ -62,11 +65,13 @@ export class ModuleLoader extends EventTarget {
basePath: string,
registry: Registry,
contextRegistry = new ContextRegistry(),
openApiDocument?: OpenApiDocument,
) {
super();
this.basePath = basePath.replaceAll("\\", "/");
this.registry = registry;
this.contextRegistry = contextRegistry;
this.openApiDocument = openApiDocument;
}

public async watch(): Promise<void> {
Expand Down Expand Up @@ -268,6 +273,7 @@ export class ModuleLoader extends EventTarget {

new endpoint.Context({
loadContext,
openApiDocument: this.openApiDocument,
readJson,
}),
);
Expand Down
27 changes: 27 additions & 0 deletions test/server/module-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,33 @@ describe("a module loader", () => {
});
});

it("passes openApiDocument to the Context constructor", async () => {
await usingTemporaryFiles(async ($) => {
await $.add(
"_.context.js",
"export class Context { constructor({ openApiDocument }) { this.openApiDocument = openApiDocument; } }",
);
await $.add("package.json", '{ "type": "module" }');

const registry: Registry = new Registry();
const contextRegistry: ContextRegistry = new ContextRegistry();
const openApiDocument = { paths: { "/hello": {} } };

const loader: ModuleLoader = new ModuleLoader(
$.path("."),
registry,
contextRegistry,
openApiDocument,
);

await loader.load();

const rootContext = contextRegistry.find("/") as any;

expect(rootContext?.openApiDocument).toBe(openApiDocument);
});
});

// can't test because I can't get Jest to refresh modules
it.skip("updates the registry when a dependency is updated", async () => {
await usingTemporaryFiles(async ($) => {
Expand Down