@b9g/async-context 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +46 -26
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,7 @@ The TC39 AsyncContext proposal aims to standardize async context propagation in
12
12
 
13
13
  This package provides a **lightweight, maintainable polyfill** that:
14
14
 
15
- ✅ Implements the TC39 `AsyncContext.Variable` API
15
+ ✅ Implements the TC39 `AsyncContext.Variable` and `AsyncContext.Snapshot` APIs
16
16
  ✅ Uses battle-tested `AsyncLocalStorage` under the hood
17
17
  ✅ Zero dependencies (beyond Node.js built-ins)
18
18
  ✅ Full TypeScript support
@@ -121,19 +121,17 @@ console.log(themeContext.get()); // "light"
121
121
  ### Classes
122
122
 
123
123
  - `AsyncVariable<T>` - Main class for creating async context variables
124
- - `AsyncContext.Variable<T>` - Alias matching TC39 proposal namespace
124
+ - `AsyncSnapshot` - Captures and restores context state
125
+ - `AsyncContext.Variable<T>` - Alias for AsyncVariable (TC39 API)
126
+ - `AsyncContext.Snapshot` - Alias for AsyncSnapshot (TC39 API)
125
127
 
126
128
  ### Types
127
129
 
128
130
  - `AsyncVariableOptions<T>` - Options for AsyncVariable constructor (defaultValue, name)
129
131
 
130
- ### Namespaces
131
-
132
- - `AsyncContext` - Namespace containing Variable class (TC39 API)
133
-
134
132
  ### Default Export
135
133
 
136
- - `AsyncContext` - The AsyncContext namespace
134
+ - `AsyncContext` - Object containing Variable and Snapshot classes
137
135
 
138
136
  ## API
139
137
 
@@ -147,13 +145,14 @@ Options:
147
145
  - `defaultValue?: T` - Default value when no context is set
148
146
  - `name?: string` - Optional name for debugging
149
147
 
150
- #### `run<R>(value: T, fn: () => R): R`
148
+ #### `run<R>(value: T, fn: (...args) => R, ...args): R`
151
149
 
152
150
  Execute a function with a context value. The value is available via `get()` throughout the entire async execution of `fn`.
153
151
 
154
152
  **Parameters:**
155
153
  - `value: T` - The context value to set
156
- - `fn: () => R` - Function to execute (can be sync or async)
154
+ - `fn: (...args) => R` - Function to execute (can be sync or async)
155
+ - `...args` - Additional arguments to pass to fn
157
156
 
158
157
  **Returns:** The return value of `fn`
159
158
 
@@ -165,14 +164,39 @@ Get the current context value. Returns `defaultValue` if no context is set.
165
164
 
166
165
  Get the name of this variable (for debugging).
167
166
 
168
- ### `AsyncContext.Variable<T>`
167
+ ### `AsyncSnapshot`
168
+
169
+ Captures the current values of all Variables at construction time. Use `run()` to restore that state later.
170
+
171
+ #### `constructor()`
172
+
173
+ Creates a snapshot of all current Variable values.
169
174
 
170
- Alias for `AsyncVariable<T>` that matches the TC39 proposal namespace.
175
+ #### `run<R>(fn: (...args) => R, ...args): R`
176
+
177
+ Execute a function with the captured context values restored.
178
+
179
+ **Parameters:**
180
+ - `fn: (...args) => R` - Function to execute
181
+ - `...args` - Additional arguments to pass to fn
182
+
183
+ **Returns:** The return value of `fn`
184
+
185
+ #### `static wrap<F>(fn: F): F`
186
+
187
+ Wrap a function to preserve the current context. When the wrapped function is called later, it will execute with the context values that were active when `wrap()` was called.
171
188
 
172
189
  ```typescript
173
- import { AsyncContext } from "@b9g/async-context";
190
+ const userVar = new AsyncContext.Variable<string>();
174
191
 
175
- const ctx = new AsyncContext.Variable<string>();
192
+ const wrappedFn = userVar.run("alice", () => {
193
+ return AsyncContext.Snapshot.wrap(() => {
194
+ return userVar.get();
195
+ });
196
+ });
197
+
198
+ // Later, even outside the run() context:
199
+ wrappedFn(); // returns "alice"
176
200
  ```
177
201
 
178
202
  ## How It Works
@@ -203,25 +227,21 @@ The polyfill provides:
203
227
 
204
228
  This package works in any JavaScript runtime that supports `AsyncLocalStorage`:
205
229
 
206
- - ✅ Node.js 12.17+ (native support)
207
- - ✅ Bun (native support)
208
- - ✅ Cloudflare Workers (via Node.js compatibility)
209
- - ⚠️ Deno (via Node.js compatibility layer: `import { AsyncLocalStorage } from "node:async_hooks"`)
230
+ - ✅ Node.js 12.17+
231
+ - ✅ Bun
232
+ - ✅ Deno
233
+ - Cloudflare Workers
210
234
 
211
235
  ## Differences from TC39 Proposal
212
236
 
213
- This polyfill currently implements:
214
-
215
- - ✅ `AsyncContext.Variable`
216
- - ✅ `.run(value, fn)` method
217
- - ✅ `.get()` method
237
+ This polyfill implements the core TC39 AsyncContext API:
218
238
 
219
- Not yet implemented (future additions):
239
+ - `AsyncContext.Variable` - context variables with `run()` and `get()`
240
+ - ✅ `AsyncContext.Snapshot` - context capture with `run()` and `wrap()`
220
241
 
221
- - `AsyncContext.Snapshot`
222
- - ⏳ `AsyncContext.Mapping`
242
+ The implementation uses Node.js `AsyncLocalStorage` rather than the pure-JS reference implementation, which means async context propagation works natively without monkey-patching `Promise.prototype.then`.
223
243
 
224
- These may be added in future versions as the proposal evolves.
244
+ Test suite adapted from the [TC39 proposal repository](https://github.com/tc39/proposal-async-context).
225
245
 
226
246
  ## Migration Path
227
247
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/async-context",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Lightweight AsyncContext polyfill for JavaScript runtimes. Implements TC39 AsyncContext proposal using AsyncLocalStorage.",
5
5
  "keywords": [
6
6
  "asynccontext",