@maestro-js/context 1.0.0-alpha.0

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.
package/README.md ADDED
@@ -0,0 +1,360 @@
1
+ # @maestro-js/context
2
+
3
+ ## Overview
4
+
5
+ The `@maestro-js/context` package provides request-scoped key-value storage backed by Node.js `AsyncLocalStorage`. Each call to `run()` creates an isolated store with two buckets -- visible (safe for logging) and hidden (for sensitive data like tokens) -- ensuring data never leaks between concurrent requests.
6
+
7
+ ## Quick Setup
8
+
9
+ ```ts
10
+ import { Context } from '@maestro-js/context'
11
+
12
+ // Create and register a context provider
13
+ const ctx = Context.Provider.create()
14
+ Context.Provider.register('default', ctx)
15
+
16
+ // Use the default facade directly
17
+ Context.run(() => {
18
+ Context.add('userId', 123)
19
+ Context.get<number>('userId') // 123
20
+ Context.all() // { userId: 123 }
21
+ })
22
+ ```
23
+
24
+ ## API Reference
25
+
26
+ ### Lifecycle Methods
27
+
28
+ #### `run<T>(callback: () => T): T`
29
+
30
+ Initialize a fresh context store for the duration of `callback`. All `add`/`get`/`push` calls inside the callback (including across `await` boundaries) share the same isolated store. Returns the callback's return value.
31
+
32
+ ```ts
33
+ const result = Context.run(() => {
34
+ Context.add('requestId', 'abc-123')
35
+ return Context.get('requestId')
36
+ })
37
+ // result === 'abc-123'
38
+ ```
39
+
40
+ #### `runWith<T>(store: Context.Store | undefined, callback: () => T): T`
41
+
42
+ Run `callback` within a previously captured store so context is available inside. If `store` is `undefined`, run the callback without any context (all reads return `undefined`).
43
+
44
+ ```ts
45
+ Context.run(() => {
46
+ Context.add('userId', 42)
47
+ const store = Context.getStore()
48
+
49
+ // Later, in a different async boundary:
50
+ Context.runWith(store, () => {
51
+ Context.get('userId') // 42
52
+ })
53
+ })
54
+ ```
55
+
56
+ #### `getStore(): Context.Store | undefined`
57
+
58
+ Return a reference to the current `AsyncLocalStorage` store, or `undefined` if called outside a `run()`. The returned store is a live reference -- mutations via `runWith` affect the original.
59
+
60
+ ### Visible Context Methods
61
+
62
+ #### `add(key: Context.Key, value: any): void`
63
+
64
+ Store a key-value pair in the visible context, overriding any existing value.
65
+
66
+ ```ts
67
+ Context.run(() => {
68
+ Context.add('tenantId', 'acme-corp')
69
+ Context.add('tenantId', 'other-corp') // overrides
70
+ Context.get('tenantId') // 'other-corp'
71
+ })
72
+ ```
73
+
74
+ #### `get<Value = unknown>(key: Context.Key): Value | undefined`
75
+
76
+ Retrieve the value for a key from the visible context, or `undefined` if absent.
77
+
78
+ ```ts
79
+ Context.run(() => {
80
+ Context.add('role', 'admin')
81
+ Context.get<string>('role') // 'admin'
82
+ Context.get('nonexistent') // undefined
83
+ })
84
+ ```
85
+
86
+ #### `has(key: Context.Key): boolean`
87
+
88
+ Return `true` if the key exists in the visible context.
89
+
90
+ #### `del(...key: Context.Key[]): void`
91
+
92
+ Remove one or more keys from the visible context.
93
+
94
+ ```ts
95
+ Context.run(() => {
96
+ Context.add('a', 1)
97
+ Context.add('b', 2)
98
+ Context.del('a', 'b')
99
+ Context.has('a') // false
100
+ })
101
+ ```
102
+
103
+ #### `push(key: Context.Key, ...value: any[]): void`
104
+
105
+ Append value(s) to an array in the visible context. Create the array if the key does not exist. Wrap an existing non-array value into an array before appending.
106
+
107
+ ```ts
108
+ Context.run(() => {
109
+ Context.push('tags', 'admin')
110
+ Context.get('tags') // ['admin']
111
+
112
+ Context.push('tags', 'user', 'verified')
113
+ Context.get('tags') // ['admin', 'user', 'verified']
114
+
115
+ Context.add('scalar', 'x')
116
+ Context.push('scalar', 'y')
117
+ Context.get('scalar') // ['x', 'y']
118
+ })
119
+ ```
120
+
121
+ #### `all(): { [key: Context.Key]: unknown }`
122
+
123
+ Return all visible context entries as a plain object. Hidden entries are excluded.
124
+
125
+ ```ts
126
+ Context.run(() => {
127
+ Context.add('userId', 123)
128
+ Context.addHidden('authToken', 'secret')
129
+ Context.all() // { userId: 123 }
130
+ })
131
+ ```
132
+
133
+ #### `createItem<Value = unknown>(key: Context.Key)`
134
+
135
+ Return a typed handle scoped to a single key in the visible context. The handle exposes `add(value)`, `get()`, `del()`, and `push(...)` methods.
136
+
137
+ ```ts
138
+ Context.run(() => {
139
+ const userId = Context.createItem<number>('userId')
140
+ userId.add(42)
141
+ userId.get() // 42
142
+ userId.del()
143
+ userId.get() // undefined
144
+ })
145
+ ```
146
+
147
+ ### Hidden Context Methods
148
+
149
+ Hidden methods mirror visible methods but operate on a separate bucket excluded from `all()`. Use hidden context for sensitive data (tokens, credentials) that should not appear in logs.
150
+
151
+ - `addHidden(key, value)` -- Store in hidden context.
152
+ - `getHidden<Value>(key)` -- Retrieve from hidden context.
153
+ - `hasHidden(key)` -- Check existence in hidden context.
154
+ - `delHidden(...key)` -- Remove from hidden context.
155
+ - `pushHidden(key, ...value)` -- Append to array in hidden context.
156
+ - `allHidden()` -- Return all hidden entries as a plain object.
157
+ - `createHiddenItem<Value>(key)` -- Typed handle for hidden context.
158
+
159
+ ```ts
160
+ Context.run(() => {
161
+ Context.addHidden('authToken', 'Bearer xyz')
162
+ Context.getHidden('authToken') // 'Bearer xyz'
163
+ Context.all() // {} -- hidden values excluded
164
+ Context.allHidden() // { authToken: 'Bearer xyz' }
165
+ })
166
+ ```
167
+
168
+ ### Provider Methods
169
+
170
+ #### `Context.Provider.create(): Context.ContextService`
171
+
172
+ Factory that instantiates a new context service with its own `AsyncLocalStorage`.
173
+
174
+ #### `Context.Provider.register(name: Context.Provider.Key, service: Context.ContextService): void`
175
+
176
+ Register an instance in the named registry.
177
+
178
+ #### `Context.provider(key: Context.Provider.Key): Context.ContextService`
179
+
180
+ Resolve a named instance. Resolution is deferred via Proxy, so `provider()` can be called before `register()`.
181
+
182
+ ## Common Patterns
183
+
184
+ ### Request-Scoped Data in HTTP Middleware
185
+
186
+ Wrap each incoming request in `Context.run()` to isolate per-request data:
187
+
188
+ ```ts
189
+ function contextMiddleware(req, res, next) {
190
+ Context.run(() => {
191
+ Context.add('requestId', req.headers['x-request-id'])
192
+ Context.add('method', req.method)
193
+ Context.add('path', req.path)
194
+ Context.addHidden('authToken', req.headers.authorization)
195
+ next()
196
+ })
197
+ }
198
+ ```
199
+
200
+ ### Propagating Context Across Async Boundaries
201
+
202
+ When context must survive into a callback that runs outside the original `run()` scope (e.g., stream event handlers), capture and restore the store:
203
+
204
+ ```ts
205
+ Context.run(() => {
206
+ Context.add('queryId', 'q-001')
207
+ const store = Context.getStore()
208
+
209
+ stream.on('end', () => {
210
+ Context.runWith(store, () => {
211
+ // Context is available here
212
+ Context.get('queryId') // 'q-001'
213
+ })
214
+ })
215
+ })
216
+ ```
217
+
218
+ This pattern is used by `@maestro-js/db` for query stream logging and by `@maestro-js/tracing` for span instrumentation.
219
+
220
+ ### Nested Runs Create Fresh Stores
221
+
222
+ Each `run()` creates a completely fresh store. Nested calls do not inherit from the outer scope:
223
+
224
+ ```ts
225
+ Context.run(() => {
226
+ Context.add('outer', 1)
227
+ Context.run(() => {
228
+ Context.get('outer') // undefined -- fresh store
229
+ Context.add('inner', 2)
230
+ })
231
+ Context.get('inner') // undefined -- inner store is gone
232
+ Context.get('outer') // 1
233
+ })
234
+ ```
235
+
236
+ ### Silent No-Ops Outside run()
237
+
238
+ All context operations silently do nothing when called outside a `run()` scope. No errors are thrown:
239
+
240
+ ```ts
241
+ Context.add('key', 'value') // no-op
242
+ Context.get('key') // undefined
243
+ Context.has('key') // false
244
+ Context.all() // {}
245
+ ```
246
+
247
+ ### Store Mutations via runWith Are Shared
248
+
249
+ `getStore()` returns a reference, not a copy. Mutations inside `runWith` affect the original store:
250
+
251
+ ```ts
252
+ Context.run(() => {
253
+ Context.add('count', 1)
254
+ const store = Context.getStore()
255
+
256
+ Context.runWith(store, () => {
257
+ Context.add('count', 2)
258
+ })
259
+
260
+ Context.get('count') // 2 -- mutated by runWith
261
+ })
262
+ ```
263
+
264
+ ### Multiple Isolated Providers
265
+
266
+ Separate `create()` calls produce fully independent context instances:
267
+
268
+ ```ts
269
+ const appContext = Context.Provider.create()
270
+ const requestContext = Context.Provider.create()
271
+
272
+ appContext.run(() => {
273
+ appContext.add('env', 'production')
274
+ requestContext.run(() => {
275
+ requestContext.add('env', 'staging')
276
+ appContext.get('env') // 'production'
277
+ requestContext.get('env') // 'staging'
278
+ })
279
+ })
280
+ ```
281
+
282
+ ## Testing
283
+
284
+ Context requires no mock driver -- `Provider.create()` returns a fully functional instance using in-process `AsyncLocalStorage`. Wrap test logic in `run()`:
285
+
286
+ ```ts
287
+ import { Context } from '@maestro-js/context'
288
+ import { test } from 'beartest-js'
289
+ import { expect } from 'expect'
290
+
291
+ test('adds and retrieves context values', () => {
292
+ const ctx = Context.Provider.create()
293
+ ctx.run(() => {
294
+ ctx.add('userId', 123)
295
+ expect(ctx.get('userId')).toBe(123)
296
+ expect(ctx.has('userId')).toBe(true)
297
+ expect(ctx.all()).toEqual({ userId: 123 })
298
+ })
299
+ })
300
+
301
+ test('hidden values excluded from all()', () => {
302
+ const ctx = Context.Provider.create()
303
+ ctx.run(() => {
304
+ ctx.add('visible', 'yes')
305
+ ctx.addHidden('secret', 'token')
306
+ expect(ctx.all()).toEqual({ visible: 'yes' })
307
+ expect(ctx.getHidden('secret')).toBe('token')
308
+ })
309
+ })
310
+
311
+ test('async propagation across await', async () => {
312
+ const ctx = Context.Provider.create()
313
+ await ctx.run(async () => {
314
+ ctx.add('before', true)
315
+ await new Promise((r) => setTimeout(r, 10))
316
+ expect(ctx.get('before')).toBe(true)
317
+ })
318
+ })
319
+ ```
320
+
321
+ Run tests with:
322
+
323
+ ```bash
324
+ pnpm --filter @maestro-js/context test
325
+ # or
326
+ cd packages/context && npx beartest ./tests/context.test.ts
327
+ ```
328
+
329
+ ## Cross-Package Integration
330
+
331
+ ### Db (query stream logging)
332
+
333
+ The `@maestro-js/db` package captures the context store before creating query streams and restores it in `end` and `error` event handlers via `Context.runWith()`, ensuring log messages emitted during stream callbacks include the original request context.
334
+
335
+ ### Tracing (span instrumentation)
336
+
337
+ The `@maestro-js/tracing` package captures the context store when starting or wrapping spans and restores it in `onEnd` callbacks via `Context.runWith()`, so span completion logs include the request context.
338
+
339
+ ### Log (contextual logging)
340
+
341
+ Log transports can call `Context.all()` to attach visible context entries to every log line within a request scope, providing automatic request-scoped structured logging without manually passing data.
342
+
343
+ ### Dependencies
344
+
345
+ - **Depends on**: `@maestro-js/service-registry` (Provider pattern foundation)
346
+ - **Used by**: `@maestro-js/db`, `@maestro-js/tracing`
347
+ - **Works with**: `@maestro-js/log` (contextual logging)
348
+
349
+ ## Types
350
+
351
+ ```ts
352
+ // Key type for context entries
353
+ type Context.Key = string | number | symbol
354
+
355
+ // The raw context snapshot containing visible and hidden Map buckets
356
+ type Context.Store = {
357
+ hidden: Map<Context.Key, unknown>
358
+ visible: Map<Context.Key, unknown>
359
+ }
360
+ ```
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Creates a new Context service instance with its own AsyncLocalStorage.
3
+ */
4
+ declare function create(): Context.ContextService;
5
+ declare function provider(key: Context.Provider.Key): Context.ContextService;
6
+ type KeysWithFallback = keyof Context.Provider.Keys extends never ? {
7
+ default: unknown;
8
+ } : Context.Provider.Keys & {
9
+ default: unknown;
10
+ };
11
+ /**
12
+ * Request-scoped key-value storage backed by AsyncLocalStorage.
13
+ * Call `Context.Provider.create()` and register it, or use the default instance.
14
+ */
15
+ declare const Context: {
16
+ provider: typeof provider;
17
+ /** Initializes a fresh context store for the duration of `callback`. */
18
+ run<T>(callback: () => T): T;
19
+ /** Stores a key-value pair in the visible context, overriding any existing value. */
20
+ add(key: Context.Key, value: any): void;
21
+ /** Removes one or more keys from the visible context. */
22
+ del(...key: Context.Key[]): void;
23
+ /** Appends value(s) to an array in the visible context. Creates the array if the key doesn't exist. */
24
+ push(key: Context.Key, ...value: any[]): void;
25
+ /** Returns `true` if the key exists in the visible context. */
26
+ has(key: Context.Key): boolean;
27
+ /** Retrieves the value for `key` from the visible context, or `undefined` if absent. */
28
+ get<Value = unknown>(key: Context.Key): Value | undefined;
29
+ /** Returns all visible context entries as a plain object. */
30
+ all(): {
31
+ [key: Context.Key]: unknown;
32
+ };
33
+ /** Returns convenience methods scoped to a single key in the visible context. */
34
+ createItem<Value = unknown>(key: Context.Key): {
35
+ add(value: Value): void;
36
+ del(): void;
37
+ push: Value extends Array<any> ? (...value: Value) => void : never;
38
+ get(): Value | undefined;
39
+ };
40
+ /** Stores a key-value pair in the hidden context (excluded from {@link all}). */
41
+ addHidden(key: Context.Key, value: any): void;
42
+ /** Removes one or more keys from the hidden context. */
43
+ delHidden(...key: Context.Key[]): void;
44
+ /** Appends value(s) to an array in the hidden context. */
45
+ pushHidden(key: Context.Key, ...value: any[]): void;
46
+ /** Returns `true` if the key exists in the hidden context. */
47
+ hasHidden(key: Context.Key): boolean;
48
+ /** Retrieves the value for `key` from the hidden context, or `undefined` if absent. */
49
+ getHidden<Value = unknown>(key: Context.Key): Value | undefined;
50
+ /** Returns all hidden context entries as a plain object. */
51
+ allHidden(): {
52
+ [key: Context.Key]: unknown;
53
+ };
54
+ /** Returns convenience methods scoped to a single key in the hidden context. */
55
+ createHiddenItem<Value = unknown>(key: Context.Key): {
56
+ add(value: Value): void;
57
+ del(): void;
58
+ push: Value extends Array<any> ? (...value: Value) => void : never;
59
+ get(): Value | undefined;
60
+ };
61
+ /** Runs `callback` within the given store so context is available inside. */
62
+ runWith<T>(store: Context.Store | undefined, callback: () => T): T;
63
+ /** Returns a reference to the current AsyncLocalStorage store, or `undefined` outside a `run()`. */
64
+ getStore(): Context.Store | undefined;
65
+ Provider: {
66
+ create: typeof create;
67
+ register: (name: Context.Provider.Key, item: Context.ContextService) => void;
68
+ };
69
+ };
70
+ /**
71
+ * Request-scoped key-value storage backed by AsyncLocalStorage.
72
+ *
73
+ * Values are either "visible" (included when calling `all()`) or "hidden"
74
+ * (accessible only through the `*Hidden` methods, excluded from logging).
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * Context.run(() => {
79
+ * Context.add('userId', 123)
80
+ * Context.push('tags', 'admin', 'user')
81
+ *
82
+ * Context.get<number>('userId') // 123
83
+ * Context.all() // { userId: 123, tags: ['admin', 'user'] }
84
+ *
85
+ * // Typed handle for a single key
86
+ * const userId = Context.createItem<number>('userId')
87
+ * userId.get() // 123
88
+ *
89
+ * // Transfer context to another async boundary
90
+ * const store = Context.getStore()
91
+ * Context.runWith(store, () => { ... })
92
+ * })
93
+ * ```
94
+ */
95
+ declare namespace Context {
96
+ type Key = string | number | symbol;
97
+ /** The raw context snapshot containing visible and hidden Map buckets */
98
+ type Store = {
99
+ hidden: Map<Key, unknown>;
100
+ visible: Map<Key, unknown>;
101
+ };
102
+ /**
103
+ * Request-scoped key-value storage backed by `AsyncLocalStorage`.
104
+ *
105
+ * Each request gets its own isolated context so data never leaks between
106
+ * requests. Values are split into two buckets:
107
+ *
108
+ * - **Visible** – included when calling {@link all} (safe for logging).
109
+ * - **Hidden** – accessible only through the `*Hidden` methods and excluded
110
+ * from logging, intended for sensitive data like tokens or credentials.
111
+ *
112
+ * Start a new context with {@link run}, then use the remaining methods to
113
+ * read and write within that scope.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * Context.run(() => {
118
+ * Context.add('userId', 123)
119
+ * Context.get<number>('userId') // 123
120
+ * Context.all() // { userId: 123 }
121
+ * })
122
+ * ```
123
+ */
124
+ interface ContextService {
125
+ /** Initializes a fresh context store for the duration of `callback`. */
126
+ run<T>(callback: () => T): T;
127
+ /** Stores a key-value pair in the visible context, overriding any existing value. */
128
+ add(key: Key, value: any): void;
129
+ /** Removes one or more keys from the visible context. */
130
+ del(...key: Key[]): void;
131
+ /** Appends value(s) to an array in the visible context. Creates the array if the key doesn't exist. */
132
+ push(key: Key, ...value: any[]): void;
133
+ /** Returns `true` if the key exists in the visible context. */
134
+ has(key: Key): boolean;
135
+ /** Retrieves the value for `key` from the visible context, or `undefined` if absent. */
136
+ get<Value = unknown>(key: Key): Value | undefined;
137
+ /** Returns all visible context entries as a plain object. */
138
+ all(): {
139
+ [key: Key]: unknown;
140
+ };
141
+ /** Returns convenience methods scoped to a single key in the visible context. */
142
+ createItem<Value = unknown>(key: Key): {
143
+ add(value: Value): void;
144
+ del(): void;
145
+ push: Value extends Array<any> ? (...value: Value) => void : never;
146
+ get(): Value | undefined;
147
+ };
148
+ /** Stores a key-value pair in the hidden context (excluded from {@link all}). */
149
+ addHidden(key: Key, value: any): void;
150
+ /** Removes one or more keys from the hidden context. */
151
+ delHidden(...key: Key[]): void;
152
+ /** Appends value(s) to an array in the hidden context. */
153
+ pushHidden(key: Key, ...value: any[]): void;
154
+ /** Returns `true` if the key exists in the hidden context. */
155
+ hasHidden(key: Key): boolean;
156
+ /** Retrieves the value for `key` from the hidden context, or `undefined` if absent. */
157
+ getHidden<Value = unknown>(key: Key): Value | undefined;
158
+ /** Returns all hidden context entries as a plain object. */
159
+ allHidden(): {
160
+ [key: Key]: unknown;
161
+ };
162
+ /** Returns convenience methods scoped to a single key in the hidden context. */
163
+ createHiddenItem<Value = unknown>(key: Key): {
164
+ add(value: Value): void;
165
+ del(): void;
166
+ push: Value extends Array<any> ? (...value: Value) => void : never;
167
+ get(): Value | undefined;
168
+ };
169
+ /** Runs `callback` within the given store so context is available inside. */
170
+ runWith<T>(store: Store | undefined, callback: () => T): T;
171
+ /** Returns a reference to the current AsyncLocalStorage store, or `undefined` outside a `run()`. */
172
+ getStore(): Store | undefined;
173
+ }
174
+ namespace Provider {
175
+ type Key = keyof KeysWithFallback;
176
+ interface Keys {
177
+ }
178
+ }
179
+ }
180
+
181
+ export { Context };
package/dist/index.js ADDED
@@ -0,0 +1,143 @@
1
+ // src/index.ts
2
+ import { AsyncLocalStorage } from "async_hooks";
3
+ import { ServiceRegistry } from "@maestro-js/service-registry";
4
+ function create() {
5
+ const asyncStore = new AsyncLocalStorage();
6
+ function makeContextFns(getContext) {
7
+ function add2(key, value) {
8
+ const store = getContext();
9
+ if (store) {
10
+ store.set(key, value);
11
+ }
12
+ }
13
+ function del2(...key) {
14
+ const store = getContext();
15
+ if (store) {
16
+ key.forEach((k) => store.delete(k));
17
+ }
18
+ }
19
+ function push2(key, ...value) {
20
+ const store = getContext();
21
+ if (store) {
22
+ const arr = store.get(key);
23
+ if (arr === void 0) {
24
+ store.set(key, [...value]);
25
+ } else if (Array.isArray(arr)) {
26
+ store.set(key, [...arr, ...value]);
27
+ } else {
28
+ store.set(key, [arr, ...value]);
29
+ }
30
+ }
31
+ }
32
+ function has2(key) {
33
+ const store = getContext();
34
+ return !!store?.has(key);
35
+ }
36
+ function get2(key) {
37
+ const store = getContext();
38
+ return store?.get(key);
39
+ }
40
+ function all2() {
41
+ const store = getContext();
42
+ const entries = store ? [...store.entries()] : [];
43
+ return Object.fromEntries(entries);
44
+ }
45
+ function createItem2(key) {
46
+ return {
47
+ add(value) {
48
+ add2(key, value);
49
+ },
50
+ del() {
51
+ del2(key);
52
+ },
53
+ push: ((...value) => {
54
+ push2(key, ...value);
55
+ }),
56
+ get() {
57
+ return get2(key);
58
+ }
59
+ };
60
+ }
61
+ return { add: add2, del: del2, push: push2, has: has2, get: get2, all: all2, createItem: createItem2 };
62
+ }
63
+ const { add, del, push, has, get, all, createItem } = makeContextFns(() => asyncStore.getStore()?.visible);
64
+ const {
65
+ add: addHidden,
66
+ del: delHidden,
67
+ push: pushHidden,
68
+ has: hasHidden,
69
+ get: getHidden,
70
+ all: allHidden,
71
+ createItem: createHiddenItem
72
+ } = makeContextFns(() => asyncStore.getStore()?.hidden);
73
+ function run(callback) {
74
+ return asyncStore.run({ visible: /* @__PURE__ */ new Map(), hidden: /* @__PURE__ */ new Map() }, callback);
75
+ }
76
+ function runWith(store, callback) {
77
+ if (store) {
78
+ return asyncStore.run(store, callback);
79
+ } else {
80
+ return callback();
81
+ }
82
+ }
83
+ function getStore() {
84
+ return asyncStore.getStore();
85
+ }
86
+ return {
87
+ run,
88
+ add,
89
+ del,
90
+ push,
91
+ has,
92
+ get,
93
+ all,
94
+ createItem,
95
+ addHidden,
96
+ delHidden,
97
+ pushHidden,
98
+ hasHidden,
99
+ getHidden,
100
+ allHidden,
101
+ createHiddenItem,
102
+ runWith,
103
+ getStore
104
+ };
105
+ }
106
+ var registry = ServiceRegistry.createRegistry(
107
+ ServiceRegistry.proxyFunctionsForObject
108
+ );
109
+ var Provider = {
110
+ create,
111
+ register: registry.register
112
+ };
113
+ function provider(key) {
114
+ const service = registry.resolve(key);
115
+ return {
116
+ run: service.run,
117
+ add: service.add,
118
+ del: service.del,
119
+ push: service.push,
120
+ has: service.has,
121
+ get: service.get,
122
+ all: service.all,
123
+ createItem: service.createItem,
124
+ addHidden: service.addHidden,
125
+ delHidden: service.delHidden,
126
+ pushHidden: service.pushHidden,
127
+ hasHidden: service.hasHidden,
128
+ getHidden: service.getHidden,
129
+ allHidden: service.allHidden,
130
+ createHiddenItem: service.createHiddenItem,
131
+ runWith: service.runWith,
132
+ getStore: service.getStore
133
+ };
134
+ }
135
+ Provider.register("default", create());
136
+ var Context = {
137
+ Provider,
138
+ ...provider("default"),
139
+ provider
140
+ };
141
+ export {
142
+ Context
143
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@maestro-js/context",
3
+ "description": "Guide for working with @maestro-js/context, a request-scoped key-value storage package backed by Node.js AsyncLocalStorage. Use when working with @maestro-js/context, adding per-request state, storing visible or hidden context values, propagating context across async boundaries, integrating context with logging or tracing, creating typed context item handles, or transferring context stores between execution scopes. Covers the Provider pattern, visible/hidden buckets, run/runWith lifecycle, createItem typed handles, getStore for context propagation, and testing patterns.",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "default": "./dist/index.js"
9
+ }
10
+ },
11
+ "dependencies": {
12
+ "@maestro-js/service-registry": "1.0.0-alpha.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^22.9.0"
16
+ },
17
+ "version": "1.0.0-alpha.0",
18
+ "publishConfig": {
19
+ "access": "restricted"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "license": "UNLICENSED",
25
+ "engines": {
26
+ "node": ">=22.18.0"
27
+ },
28
+ "repository": "https://github.com/Marcato-Partners/maestro-js",
29
+ "scripts": {
30
+ "build": "tsup --config ../../tsup.config.ts",
31
+ "typecheck": "tsc --noEmit",
32
+ "test": "npx beartest ./tests",
33
+ "format": "prettier --write src/",
34
+ "lint": "prettier --check src/"
35
+ }
36
+ }