Skip to content

Commit

Permalink
ability at runtime for a context object to access other context objects
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcelhaney committed Jun 28, 2024
1 parent c9f5007 commit 48a0c78
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/tiny-chefs-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"counterfact": minor
---

ability at runtime for a context object to access other context objects
11 changes: 11 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,17 @@ By default, each `_.context.ts` delegates to its parent directory, so you can de
> [!TIP]
> You can make the context objects do whatever you want, including things like writing to databases. But remember that Counterfact is meant for testing, so holding on to data between sessions is an anti-pattern. Keeping everything in memory also makes it fast.

> [!TIP]
> An object with loadContext() function is passed to the constructor of a context class. You can use it load the context from another directory at runtime. This is an advanced use case.
>
> ```ts
> class Context {
> constructor({ loadContext }) {
> this.rootContext = loadContext("/");
> }
> }
> ```
### Security: the `$.auth` object
If a username and password are sent via basic authentication, the username and password can be found via `$.auth.username` and `$.auth.password` respectively.
Expand Down
4 changes: 3 additions & 1 deletion src/server/module-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ export class ModuleLoader extends EventTarget {

// @ts-expect-error TS says Context has no constructable signatures but that's not true?
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
new endpoint.Context(),
new endpoint.Context({
loadContext: (path: string) => this.contextRegistry.find(path),
}),
);
}
} else {
Expand Down
30 changes: 30 additions & 0 deletions test/server/module-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ describe("a module loader", () => {
});
});

it("provides the parent context if the local _.context.ts doesn't export a default", async () => {
await usingTemporaryFiles(async ($) => {
await $.add(
"_.context.js",
"export class Context { constructor({loadContext}) { this.loadContext = loadContext } }",
);
await $.add("a/_.context.js", "export class Context { name = 'a' }");
await $.add("package.json", '{ "type": "module" }');

const registry: Registry = new Registry();

const contextRegistry: ContextRegistry = new ContextRegistry();

const loader: ModuleLoader = new ModuleLoader(
$.path("."),

registry,
contextRegistry,
);

await loader.load();

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
const rootContext = contextRegistry.find("/") as any;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
expect(rootContext?.loadContext("/a")?.name).toBe("a");
});
});

// 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

0 comments on commit 48a0c78

Please sign in to comment.