Skip to content

Commit

Permalink
Merge pull request #964 from pmcelhaney/load-context-in-constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcelhaney authored Jun 28, 2024
2 parents b6e835f + cc14ec0 commit 855da30
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 33 deletions.
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@types/koa-bodyparser": "4.3.12",
"@types/koa-proxy": "1.0.7",
"@types/koa-static": "4.0.4",
"@types/lodash": "^4.17.6",
"copyfiles": "2.4.1",
"eslint": "8.57.0",
"eslint-config-hardcore": "41.3.0",
Expand Down Expand Up @@ -97,6 +98,7 @@
"koa-bodyparser": "4.4.1",
"koa-proxy": "1.0.0-alpha.3",
"koa2-swagger-ui": "5.10.0",
"lodash": "^4.17.21",
"node-fetch": "3.3.2",
"open": "10.1.0",
"patch-package": "8.0.0",
Expand Down
11 changes: 7 additions & 4 deletions src/server/context-registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// eslint-disable-next-line max-classes-per-file
/* eslint-disable max-classes-per-file */
/* eslint-disable max-statements */
import cloneDeep from "lodash/cloneDeep.js";

export class Context {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor, @typescript-eslint/no-empty-function
public constructor() {}
Expand Down Expand Up @@ -33,7 +36,8 @@ export class ContextRegistry {

public add(path: string, context: Context): void {
this.entries.set(path, context);
this.cache.set(path, structuredClone(context));

this.cache.set(path, cloneDeep(context));
}

public find(path: string): Context {
Expand All @@ -43,7 +47,6 @@ export class ContextRegistry {
);
}

// eslint-disable-next-line max-statements
public update(path: string, updatedContext?: Context): void {
if (updatedContext === undefined) {
return;
Expand All @@ -66,6 +69,6 @@ export class ContextRegistry {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
Object.setPrototypeOf(context, Object.getPrototypeOf(updatedContext));

this.cache.set(path, structuredClone(updatedContext));
this.cache.set(path, cloneDeep(updatedContext));
}
}
8 changes: 7 additions & 1 deletion src/server/module-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,16 @@ export class ModuleLoader extends EventTarget {

if (basename(pathName).startsWith("_.context")) {
if (isContextModule(endpoint)) {
const loadContext = (path: string) => this.contextRegistry.find(path);

this.contextRegistry.update(
directory,

// @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,
}),
);
}
} else {
Expand All @@ -191,6 +195,8 @@ export class ModuleLoader extends EventTarget {
return;
}

throw error;

process.stdout.write(`\nError loading ${pathName}:\n${String(error)}\n`);
}
}
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
36 changes: 8 additions & 28 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2718,6 +2718,11 @@
"@types/koa-compose" "*"
"@types/node" "*"

"@types/lodash@^4.17.6":
version "4.17.6"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543"
integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==

"@types/mdast@^3.0.0":
version "3.0.12"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.12.tgz#beeb511b977c875a5b0cc92eab6fcac2f0895514"
Expand Down Expand Up @@ -10660,7 +10665,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0":
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -10677,15 +10682,6 @@ string-width@^2.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
Expand Down Expand Up @@ -10755,7 +10751,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand All @@ -10776,13 +10772,6 @@ strip-ansi@^5.1.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -11767,7 +11756,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -11785,15 +11774,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down

0 comments on commit 855da30

Please sign in to comment.