Skip to content

Commit d82ee37

Browse files
authoredMar 10, 2025··
Merge pull request #983 from thefrontside/backport-context-with
🎒bring Context.with() to v3
2 parents c059028 + 0f3e7f3 commit d82ee37

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed
 

‎lib/context.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Context } from "./types.ts";
1+
import type { Context, Operation } from "./types.ts";
22
import { create } from "./run/create.ts";
33
import { useScope } from "./run/scope.ts";
44

@@ -14,6 +14,19 @@ export function createContext<T>(key: string, defaultValue?: T): Context<T> {
1414
return scope.set(context, value);
1515
},
1616
expect,
17+
*with<R>(value: T, operation: (value: T) => Operation<R>): Operation<R> {
18+
let scope = yield* useScope();
19+
let original = scope.hasOwn(context) ? scope.get(context) : undefined;
20+
try {
21+
return yield* operation(scope.set(context, value));
22+
} finally {
23+
if (typeof original === "undefined") {
24+
scope.delete(context);
25+
} else {
26+
scope.set(context, original);
27+
}
28+
}
29+
},
1730
[Symbol.iterator]() {
1831
console.warn(
1932
`⚠️ using a context (${key}) directly as an operation is deprecated. Use context.expect() instead`,

‎lib/types.ts

+16
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,22 @@ export interface Context<T> extends Operation<T> {
298298
* @return an operation providing the context value in the current scope.
299299
*/
300300
expect(): Operation<T>;
301+
302+
/**
303+
* Evaluate an operation using `value` for the context. Once the operation is completed, the context
304+
* will be reverted to its original value, or removed if it was not present originally.
305+
*
306+
* @example
307+
* ```ts
308+
* let user = yield* login();
309+
* yield* UserContext.with(user, function*() {
310+
* //do stuff
311+
* })
312+
* ```
313+
*
314+
* @returns the result of evaluating the operation.
315+
*/
316+
with<R>(value: T, operation: (value: T) => Operation<R>): Operation<R>;
301317
}
302318

303319
/**

‎test/context.test.ts

+15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ describe("context", () => {
1313
).toEqual(3);
1414
});
1515

16+
it("can delimit the bounds of a context within a single scope", async () => {
17+
let values = await run(function* () {
18+
let before = yield* numbers.get();
19+
20+
let within = yield* numbers.with(22, function* () {
21+
return yield* numbers.get();
22+
});
23+
24+
let after = yield* numbers.get();
25+
return [before, within, after];
26+
});
27+
28+
expect(values).toEqual([3, 22, 3]);
29+
});
30+
1631
it("can be set within a given scope, but reverts after", async () => {
1732
let values = await run(function* () {
1833
let before = yield* numbers.expect();

0 commit comments

Comments
 (0)
Please sign in to comment.