@unional/async-context 1.0.3 → 3.0.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.
Files changed (51) hide show
  1. package/README.md +64 -28
  2. package/cjs/AsyncContext.d.ts +17 -0
  3. package/cjs/AsyncContext.d.ts.map +1 -0
  4. package/cjs/AsyncContext.js +73 -0
  5. package/cjs/AsyncContext.js.map +1 -0
  6. package/cjs/errors.d.ts +5 -0
  7. package/cjs/errors.d.ts.map +1 -0
  8. package/cjs/errors.js +11 -0
  9. package/cjs/errors.js.map +1 -0
  10. package/{lib → cjs}/index.d.ts +0 -0
  11. package/cjs/index.d.ts.map +1 -0
  12. package/{lib → cjs}/index.js +5 -1
  13. package/cjs/index.js.map +1 -0
  14. package/esm/AsyncContext.d.ts +17 -0
  15. package/esm/AsyncContext.d.ts.map +1 -0
  16. package/esm/AsyncContext.js +69 -0
  17. package/esm/AsyncContext.js.map +1 -0
  18. package/esm/errors.d.ts +5 -0
  19. package/esm/errors.d.ts.map +1 -0
  20. package/{libm → esm}/errors.js +2 -2
  21. package/esm/errors.js.map +1 -0
  22. package/{libm → esm}/index.d.ts +0 -0
  23. package/esm/index.d.ts.map +1 -0
  24. package/{libm → esm}/index.js +0 -0
  25. package/esm/index.js.map +1 -0
  26. package/package.json +52 -19
  27. package/ts/AsyncContext.ts +59 -0
  28. package/{src → ts}/errors.ts +3 -3
  29. package/{src → ts}/index.ts +0 -0
  30. package/CHANGELOG.md +0 -24
  31. package/lib/AsyncContext.d.ts +0 -11
  32. package/lib/AsyncContext.d.ts.map +0 -1
  33. package/lib/AsyncContext.js +0 -93
  34. package/lib/AsyncContext.js.map +0 -1
  35. package/lib/errors.d.ts +0 -5
  36. package/lib/errors.d.ts.map +0 -1
  37. package/lib/errors.js +0 -26
  38. package/lib/errors.js.map +0 -1
  39. package/lib/index.d.ts.map +0 -1
  40. package/lib/index.js.map +0 -1
  41. package/libm/AsyncContext.d.ts +0 -11
  42. package/libm/AsyncContext.d.ts.map +0 -1
  43. package/libm/AsyncContext.js +0 -25
  44. package/libm/AsyncContext.js.map +0 -1
  45. package/libm/errors.d.ts +0 -5
  46. package/libm/errors.d.ts.map +0 -1
  47. package/libm/errors.js.map +0 -1
  48. package/libm/index.d.ts.map +0 -1
  49. package/libm/index.js.map +0 -1
  50. package/src/AsyncContext.spec.ts +0 -187
  51. package/src/AsyncContext.ts +0 -27
package/README.md CHANGED
@@ -1,19 +1,17 @@
1
- # @unional/async-context
1
+ # `@unional/async-context`
2
2
 
3
3
  [![NPM version][npm-image]][npm-url]
4
4
  [![NPM downloads][downloads-image]][downloads-url]
5
5
  [![Bundle size][bundlephobia-image]][bundlephobia-url]
6
6
 
7
7
  [![Codecov][codecov-image]][codecov-url]
8
- [![Codacy Grade Badge][codacy-grade]][codacy-grade-url]
9
- [![Codacy Coverage Badge][codacy-coverage]][codacy-coverage-url]
10
8
 
11
9
  Secure, type safe, asynchronous context for functional programming.
12
10
 
13
11
  In functional programming,
14
12
  it is common to pass in a context object containing dependencies used by the function.
15
13
 
16
- `AsyncContext` allow these dependencies to be loaded asynchronously.
14
+ `AsyncContext` allows these dependencies to be loaded asynchronously.
17
15
 
18
16
  This is useful in many cases. For example,
19
17
 
@@ -24,52 +22,69 @@ This is useful in many cases. For example,
24
22
 
25
23
  ## Installation
26
24
 
25
+ This is part of [`async-fp`](../async-fp/README.md).
26
+
27
+ Typically, you will install that instead of installing `@unional/async-context` directly.
28
+
29
+ But if you want, you can:
30
+
27
31
  ```sh
32
+ # npm
28
33
  npm install @unional/async-context
29
- # or
34
+
35
+ # yarn
30
36
  yarn add @unional/async-context
37
+
38
+ # pnpm
39
+ pnpm install @unional/async-context
40
+
41
+ #rush
42
+ rush add -p @unional/async-context
31
43
  ```
32
44
 
33
45
  ## Usage
34
46
 
47
+ You can provide the initial context when calling the constructor.
48
+ The context value must be an object (`Record`).
49
+ You can extend and augment the context with the [`extend()`](#extend) later.
50
+
51
+ You can supply the context value in 4 different ways:
52
+
35
53
  ```ts
36
54
  import { AsyncContext } from '@unional/async-context'
37
55
 
38
- const context = new AsyncContext({ key: 'secret key' })
39
- const context = new AsyncContext(Promise.resolve({ key: 'secret key' }))
40
- const context = new AsyncContext(() => ({ key: 'secret key' }))
56
+ const context = new AsyncContext({ key: 'secret key' }) // or
57
+ const context = new AsyncContext(Promise.resolve({ key: 'secret key' })) // or
58
+ const context = new AsyncContext(() => ({ key: 'secret key' })) // or
41
59
  const context = new AsyncContext(async () => ({ key: 'secret key' }))
42
60
 
43
61
  await context.get() // => { key: 'secret key' }
44
62
  ```
45
63
 
46
- The context value must be an object (`Record`).
47
- This allows the context to be [extended](#extend).
48
-
49
- If you provide a handler,
50
- it will not be executed until the first `context.get()` is called.
51
- It allows you to wait for user input and change the value/dependency loaded.
64
+ If you provide an initialize function,
65
+ it will not be executed until the `context.get()` is called.
66
+ It allows you to wait for user input and change the values/dependencies loaded.
52
67
 
53
68
  If you want to start loading the dependencies immediately,
54
- starts the loading and pass in a `Promise`.
69
+ starts the loading and pass in the resulting `Promise`.
55
70
 
56
71
  ### Initialize
57
72
 
58
- You can create an `AsyncContext` and initialize it later.
73
+ You can create a `AsyncContext` and initialize it later.
59
74
  This allows you to create a context in one part of your system,
60
- and initialize it in another part.
75
+ and initialize it in another.
61
76
 
62
77
  ```ts
63
78
  import { AsyncContext } from '@unional/async-context'
64
79
 
65
- export const context = new AsyncContext<{ key: string }>()
80
+ export const context = new AsyncContext<{ key: string }>() // optionally specify the init context type
66
81
 
67
82
  // in another file
68
83
  import { context } from './context'
69
84
 
70
- context.initialize({ key: 'secret key' })
71
- context.initialize(Promise.resolve({ key: 'secret key' }))
72
- context.initialize(() => ({ key: 'secret key' }))
85
+ context.initialize({ key: 'secret key' }) // or
86
+ context.initialize(Promise.resolve({ key: 'secret key' })) // or
87
+ context.initialize(() => ({ key: 'secret key' })) // or
73
88
  context.initialize(() => Promise.resolve({ key: 'secret key' }))
74
89
  ```
75
90
 
@@ -83,7 +98,7 @@ you can use [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/Jav
83
98
 
84
99
  ### Extend
85
100
 
86
- You can extends a new context with new or override properties.
101
+ You can extend or augment the context by calling `extend()`.
87
102
 
88
103
  ```ts
89
104
  import { AsyncContext } from '@unional/async-context'
@@ -98,6 +113,33 @@ const newCtx = ctx.extend(async () => ({ b: 2, c: 3 }))
98
113
  await newCtx.get() // => { a: 1, b: 2, c: 3 }
99
114
  ```
100
115
 
116
+ The `extend()` updates the `AsyncContext` in place,
117
+ i.e. the `newCtx` and `ctx` above is the same instance.
118
+
119
+ The difference is that `newCtx` will have the context type updated while `ctx` doesn't.
120
+
121
+ If you want to use the `ctx` directly, you can use `get<T>()` to assert the context type.
122
+
123
+ ```ts
124
+ const ctx = new AsyncContext({ a: 1, b: 'b' })
125
+
126
+ ctx.extend({ b: 2, c: 3 })
127
+ await ctx.get<{ a: number, b: number, c: number }>()
128
+
129
+ // it is really the same as this, but less verbose
130
+ (await ctx.get() as { a: number, b: number, c: number })
131
+ ```
132
+
133
+ ### Chaining
134
+
135
+ As you can guess, the `initialize()` and `extend()` functions can be chained:
136
+
137
+ ```ts
138
+ const ctx = new AsyncContext()
139
+
140
+ ctx.initialize({ a: 1 }).extend({ b: 2 }).extend({ c: 3 })
141
+ ```
142
+
101
143
  ## Use Cases
102
144
 
103
145
  ### Just-in-time Dependency Loading
@@ -166,15 +208,9 @@ configure({ ... })
166
208
 
167
209
  [bundlephobia-image]: https://img.shields.io/bundlephobia/minzip/@unional/async-context.svg
168
210
  [bundlephobia-url]: https://bundlephobia.com/result?p=@unional/async-context
169
- [codacy-grade]: https://api.codacy.com/project/badge/Grade/707f89609508442486050d207ec5bd78
170
- [codacy-grade-url]: https://www.codacy.com/app/homawong/async-fp?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=unional/async-fp&amp;utm_campaign=Badge_Grade
171
- [codacy-coverage]: https://api.codacy.com/project/badge/Coverage/707f89609508442486050d207ec5bd78
172
- [codacy-coverage-url]: https://www.codacy.com/manual/homawong/async-fp?utm_source=github.com&utm_medium=referral&utm_content=unional/async-fp&utm_campaign=Badge_Coverage
173
211
  [codecov-image]: https://codecov.io/gh/unional/async-fp/branch/master/graph/badge.svg
174
212
  [codecov-url]: https://codecov.io/gh/unional/async-fp
175
213
  [downloads-image]: https://img.shields.io/npm/dm/@unional/async-context.svg?style=flat
176
214
  [downloads-url]: https://npmjs.org/package/@unional/async-context
177
- [github-nodejs]: https://github.com/unional/async-fp/workflows/Node%20CI/badge.svg
178
- [github-action-url]: https://github.com/unional/async-fp/actions
179
215
  [npm-image]: https://img.shields.io/npm/v/@unional/async-context.svg?style=flat
180
216
  [npm-url]: https://npmjs.org/package/@unional/async-context
@@ -0,0 +1,17 @@
1
+ import { LeftJoin } from 'type-plus';
2
+ export declare class AsyncContext<Init extends Record<string | symbol, any>, Context extends Record<string | symbol, any> = Init> {
3
+ #private;
4
+ private init?;
5
+ private transformers;
6
+ private resolving;
7
+ private resolve;
8
+ constructor(init?: Init | Promise<Init> | AsyncContext.Initializer<Init> | undefined);
9
+ initialize<I extends Init = Init>(init: I | Promise<I> | AsyncContext.Initializer<I>): AsyncContext<I>;
10
+ extend<R extends Record<string | symbol, any>>(context: R | Promise<R> | AsyncContext.Transformer<Context, R>): AsyncContext<Init, LeftJoin<Context, R>>;
11
+ get<C = Context>(): Promise<C>;
12
+ }
13
+ export declare namespace AsyncContext {
14
+ type Initializer<Init> = () => Init | Promise<Init>;
15
+ type Transformer<CurrentContext, AdditionalContext> = (context: CurrentContext) => AdditionalContext | Promise<AdditionalContext>;
16
+ }
17
+ //# sourceMappingURL=AsyncContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncContext.d.ts","sourceRoot":"","sources":["../ts/AsyncContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGpC,qBAAa,YAAY,CACvB,IAAI,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EACzC,OAAO,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;;IAKvC,OAAO,CAAC,IAAI,CAAC;IAHzB,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,OAAO,CAA6D;gBACxD,IAAI,CAAC,mEAAuD;IAChF,UAAU,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IAOtG,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAC3C,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAC7D,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAIrC,GAAG,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;CAUrC;AACD,yBAAiB,YAAY,CAAC;IAC5B,KAAY,WAAW,CAAC,IAAI,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D,KAAY,WAAW,CACrB,cAAc,EACd,iBAAiB,IACb,CAAC,OAAO,EAAE,cAAc,KAAK,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAClF"}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var _AsyncContext_instances, _AsyncContext_buildContext;
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AsyncContext = void 0;
10
+ const errors_1 = require("./errors");
11
+ class AsyncContext {
12
+ constructor(init) {
13
+ _AsyncContext_instances.add(this);
14
+ Object.defineProperty(this, "init", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: init
19
+ });
20
+ Object.defineProperty(this, "transformers", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: []
25
+ });
26
+ Object.defineProperty(this, "resolving", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: void 0
31
+ });
32
+ Object.defineProperty(this, "resolve", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: undefined
37
+ });
38
+ }
39
+ initialize(init) {
40
+ if (this.init)
41
+ throw new errors_1.ContextAlreadyInitialized();
42
+ this.init = init;
43
+ if (this.resolve)
44
+ this.resolve(__classPrivateFieldGet(this, _AsyncContext_instances, "m", _AsyncContext_buildContext).call(this));
45
+ return this;
46
+ }
47
+ extend(context) {
48
+ this.transformers.push(isTransformer(context) ? context : () => context);
49
+ return this;
50
+ }
51
+ async get() {
52
+ if (this.resolving)
53
+ return this.resolving;
54
+ return this.resolving = this.init ? __classPrivateFieldGet(this, _AsyncContext_instances, "m", _AsyncContext_buildContext).call(this) : new Promise(a => this.resolve = a);
55
+ }
56
+ }
57
+ exports.AsyncContext = AsyncContext;
58
+ _AsyncContext_instances = new WeakSet(), _AsyncContext_buildContext = async function _AsyncContext_buildContext() {
59
+ return this.transformers.reduce(async (p, t) => {
60
+ const c = await p;
61
+ return { ...c, ...await t(c) };
62
+ }, resolveInit(this.init));
63
+ };
64
+ async function resolveInit(init) {
65
+ return isInitializer(init) ? init() : init;
66
+ }
67
+ function isTransformer(context) {
68
+ return typeof context === 'function';
69
+ }
70
+ function isInitializer(context) {
71
+ return typeof context === 'function';
72
+ }
73
+ //# sourceMappingURL=AsyncContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncContext.js","sourceRoot":"","sources":["../ts/AsyncContext.ts"],"names":[],"mappings":";;;;;;;;;AACA,qCAAoD;AAEpD,MAAa,YAAY;IAOvB,YAAoB,IAA4D;;;;;;mBAA5D;;QAHpB;;;;mBAAkE,EAAE;WAAA;QACpE;;;;;WAA2C;QAC3C;;;;mBAAmE,SAAS;WAAA;IACQ,CAAC;IACrF,UAAU,CAAwB,IAAkD;QAClF,IAAI,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,kCAAyB,EAAE,CAAA;QACpD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAEhB,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,uBAAA,IAAI,2DAAc,MAAlB,IAAI,CAAgB,CAAC,CAAA;QACpD,OAAO,IAAuB,CAAA;IAChC,CAAC;IACD,MAAM,CACJ,OAA8D;QAE9D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAA;QACxE,OAAO,IAAgD,CAAA;IACzD,CAAC;IACD,KAAK,CAAC,GAAG;QACP,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAA;QACzC,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAA,IAAI,2DAAc,MAAlB,IAAI,CAAgB,CAAC,CAAC,CAAC,IAAI,OAAO,CAAM,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IACpG,CAAC;CAOF;AA/BD,oCA+BC;sEANC,KAAK;IACH,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;QACjB,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAQ,EAAE,CAAA;IACvC,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,IAAK,CAAC,CAAqB,CAAA;AACjD,CAAC;AAUH,KAAK,UAAU,WAAW,CAEtB,IAA2D;IAC7D,OAAO,aAAa,CAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAClD,CAAC;AAED,SAAS,aAAa,CAGpB,OAAgB;IAChB,OAAO,OAAO,OAAO,KAAK,UAAU,CAAA;AACtC,CAAC;AAED,SAAS,aAAa,CAAI,OAAgB;IACxC,OAAO,OAAO,OAAO,KAAK,UAAU,CAAA;AACtC,CAAC","sourcesContent":["import { LeftJoin } from 'type-plus'\nimport { ContextAlreadyInitialized } from './errors'\n\nexport class AsyncContext<\n Init extends Record<string | symbol, any>,\n Context extends Record<string | symbol, any> = Init\n > {\n private transformers: Array<AsyncContext.Transformer<any, any>> = []\n private resolving: Promise<any> | undefined\n private resolve: ((value: Promise<Context>) => void) | undefined = undefined\n constructor(private init?: Init | Promise<Init> | AsyncContext.Initializer<Init>) { }\n initialize<I extends Init = Init>(init: I | Promise<I> | AsyncContext.Initializer<I>): AsyncContext<I> {\n if (this.init) throw new ContextAlreadyInitialized()\n this.init = init\n\n if (this.resolve) this.resolve(this.#buildContext())\n return this as AsyncContext<I>\n }\n extend<R extends Record<string | symbol, any>>(\n context: R | Promise<R> | AsyncContext.Transformer<Context, R>\n ): AsyncContext<Init, LeftJoin<Context, R>> {\n this.transformers.push(isTransformer(context) ? context : () => context)\n return this as AsyncContext<Init, LeftJoin<Context, R>>\n }\n async get<C = Context>(): Promise<C> {\n if (this.resolving) return this.resolving\n return this.resolving = this.init ? this.#buildContext() : new Promise<any>(a => this.resolve = a)\n }\n async #buildContext(): Promise<Context> {\n return this.transformers.reduce(async (p, t) => {\n const c = await p\n return { ...c, ...await t(c) as any }\n }, resolveInit(this.init!)) as Promise<Context>\n }\n}\nexport namespace AsyncContext {\n export type Initializer<Init> = () => Init | Promise<Init>\n export type Transformer<\n CurrentContext,\n AdditionalContext\n > = (context: CurrentContext) => AdditionalContext | Promise<AdditionalContext>\n}\n\nasync function resolveInit<\n Init extends Record<string | symbol, any>,\n >(init: Init | Promise<Init> | AsyncContext.Initializer<Init>): Promise<Init> {\n return isInitializer<Init>(init) ? init() : init\n}\n\nfunction isTransformer<\n CurrentContext,\n AdditionalContext\n>(context: unknown): context is AsyncContext.Transformer<CurrentContext, AdditionalContext> {\n return typeof context === 'function'\n}\n\nfunction isInitializer<T>(context: unknown): context is AsyncContext.Initializer<T> {\n return typeof context === 'function'\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { IsoError, ModuleError } from 'iso-error';
2
+ export declare class ContextAlreadyInitialized extends ModuleError {
3
+ constructor(options?: IsoError.Options);
4
+ }
5
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../ts/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEjD,qBAAa,yBAA0B,SAAQ,WAAW;gBAC5C,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO;CAGvC"}
package/cjs/errors.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContextAlreadyInitialized = void 0;
4
+ const iso_error_1 = require("iso-error");
5
+ class ContextAlreadyInitialized extends iso_error_1.ModuleError {
6
+ constructor(options) {
7
+ super('@unional/async-context', 'context can only be initialized once.', options);
8
+ }
9
+ }
10
+ exports.ContextAlreadyInitialized = ContextAlreadyInitialized;
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../ts/errors.ts"],"names":[],"mappings":";;;AAAA,yCAAiD;AAEjD,MAAa,yBAA0B,SAAQ,uBAAW;IACxD,YAAY,OAA0B;QACpC,KAAK,CAAC,wBAAwB,EAAE,uCAAuC,EAAE,OAAO,CAAC,CAAA;IACnF,CAAC;CACF;AAJD,8DAIC","sourcesContent":["import { IsoError, ModuleError } from 'iso-error'\n\nexport class ContextAlreadyInitialized extends ModuleError {\n constructor(options?: IsoError.Options) {\n super('@unional/async-context', 'context can only be initialized once.', options)\n }\n}\n"]}
File without changes
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA"}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA8B;AAC9B,2CAAwB","sourcesContent":["export * from './AsyncContext'\nexport * from './errors'\n"]}
@@ -0,0 +1,17 @@
1
+ import { LeftJoin } from 'type-plus';
2
+ export declare class AsyncContext<Init extends Record<string | symbol, any>, Context extends Record<string | symbol, any> = Init> {
3
+ #private;
4
+ private init?;
5
+ private transformers;
6
+ private resolving;
7
+ private resolve;
8
+ constructor(init?: Init | Promise<Init> | AsyncContext.Initializer<Init> | undefined);
9
+ initialize<I extends Init = Init>(init: I | Promise<I> | AsyncContext.Initializer<I>): AsyncContext<I>;
10
+ extend<R extends Record<string | symbol, any>>(context: R | Promise<R> | AsyncContext.Transformer<Context, R>): AsyncContext<Init, LeftJoin<Context, R>>;
11
+ get<C = Context>(): Promise<C>;
12
+ }
13
+ export declare namespace AsyncContext {
14
+ type Initializer<Init> = () => Init | Promise<Init>;
15
+ type Transformer<CurrentContext, AdditionalContext> = (context: CurrentContext) => AdditionalContext | Promise<AdditionalContext>;
16
+ }
17
+ //# sourceMappingURL=AsyncContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncContext.d.ts","sourceRoot":"","sources":["../ts/AsyncContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGpC,qBAAa,YAAY,CACvB,IAAI,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EACzC,OAAO,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;;IAKvC,OAAO,CAAC,IAAI,CAAC;IAHzB,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,OAAO,CAA6D;gBACxD,IAAI,CAAC,mEAAuD;IAChF,UAAU,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IAOtG,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAC3C,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAC7D,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAIrC,GAAG,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;CAUrC;AACD,yBAAiB,YAAY,CAAC;IAC5B,KAAY,WAAW,CAAC,IAAI,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D,KAAY,WAAW,CACrB,cAAc,EACd,iBAAiB,IACb,CAAC,OAAO,EAAE,cAAc,KAAK,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAClF"}
@@ -0,0 +1,69 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _AsyncContext_instances, _AsyncContext_buildContext;
7
+ import { ContextAlreadyInitialized } from './errors';
8
+ export class AsyncContext {
9
+ constructor(init) {
10
+ _AsyncContext_instances.add(this);
11
+ Object.defineProperty(this, "init", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: init
16
+ });
17
+ Object.defineProperty(this, "transformers", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: []
22
+ });
23
+ Object.defineProperty(this, "resolving", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ Object.defineProperty(this, "resolve", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: undefined
34
+ });
35
+ }
36
+ initialize(init) {
37
+ if (this.init)
38
+ throw new ContextAlreadyInitialized();
39
+ this.init = init;
40
+ if (this.resolve)
41
+ this.resolve(__classPrivateFieldGet(this, _AsyncContext_instances, "m", _AsyncContext_buildContext).call(this));
42
+ return this;
43
+ }
44
+ extend(context) {
45
+ this.transformers.push(isTransformer(context) ? context : () => context);
46
+ return this;
47
+ }
48
+ async get() {
49
+ if (this.resolving)
50
+ return this.resolving;
51
+ return this.resolving = this.init ? __classPrivateFieldGet(this, _AsyncContext_instances, "m", _AsyncContext_buildContext).call(this) : new Promise(a => this.resolve = a);
52
+ }
53
+ }
54
+ _AsyncContext_instances = new WeakSet(), _AsyncContext_buildContext = async function _AsyncContext_buildContext() {
55
+ return this.transformers.reduce(async (p, t) => {
56
+ const c = await p;
57
+ return { ...c, ...await t(c) };
58
+ }, resolveInit(this.init));
59
+ };
60
+ async function resolveInit(init) {
61
+ return isInitializer(init) ? init() : init;
62
+ }
63
+ function isTransformer(context) {
64
+ return typeof context === 'function';
65
+ }
66
+ function isInitializer(context) {
67
+ return typeof context === 'function';
68
+ }
69
+ //# sourceMappingURL=AsyncContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AsyncContext.js","sourceRoot":"","sources":["../ts/AsyncContext.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAEpD,MAAM,OAAO,YAAY;IAOvB,YAAoB,IAA4D;;;;;;mBAA5D;;QAHpB;;;;mBAAkE,EAAE;WAAA;QACpE;;;;;WAA2C;QAC3C;;;;mBAAmE,SAAS;WAAA;IACQ,CAAC;IACrF,UAAU,CAAwB,IAAkD;QAClF,IAAI,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,yBAAyB,EAAE,CAAA;QACpD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAEhB,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,uBAAA,IAAI,2DAAc,MAAlB,IAAI,CAAgB,CAAC,CAAA;QACpD,OAAO,IAAuB,CAAA;IAChC,CAAC;IACD,MAAM,CACJ,OAA8D;QAE9D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAA;QACxE,OAAO,IAAgD,CAAA;IACzD,CAAC;IACD,KAAK,CAAC,GAAG;QACP,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAA;QACzC,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAA,IAAI,2DAAc,MAAlB,IAAI,CAAgB,CAAC,CAAC,CAAC,IAAI,OAAO,CAAM,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IACpG,CAAC;CAOF;sEANC,KAAK;IACH,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;QACjB,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAQ,EAAE,CAAA;IACvC,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,IAAK,CAAC,CAAqB,CAAA;AACjD,CAAC;AAUH,KAAK,UAAU,WAAW,CAEtB,IAA2D;IAC7D,OAAO,aAAa,CAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAClD,CAAC;AAED,SAAS,aAAa,CAGpB,OAAgB;IAChB,OAAO,OAAO,OAAO,KAAK,UAAU,CAAA;AACtC,CAAC;AAED,SAAS,aAAa,CAAI,OAAgB;IACxC,OAAO,OAAO,OAAO,KAAK,UAAU,CAAA;AACtC,CAAC","sourcesContent":["import { LeftJoin } from 'type-plus'\nimport { ContextAlreadyInitialized } from './errors'\n\nexport class AsyncContext<\n Init extends Record<string | symbol, any>,\n Context extends Record<string | symbol, any> = Init\n > {\n private transformers: Array<AsyncContext.Transformer<any, any>> = []\n private resolving: Promise<any> | undefined\n private resolve: ((value: Promise<Context>) => void) | undefined = undefined\n constructor(private init?: Init | Promise<Init> | AsyncContext.Initializer<Init>) { }\n initialize<I extends Init = Init>(init: I | Promise<I> | AsyncContext.Initializer<I>): AsyncContext<I> {\n if (this.init) throw new ContextAlreadyInitialized()\n this.init = init\n\n if (this.resolve) this.resolve(this.#buildContext())\n return this as AsyncContext<I>\n }\n extend<R extends Record<string | symbol, any>>(\n context: R | Promise<R> | AsyncContext.Transformer<Context, R>\n ): AsyncContext<Init, LeftJoin<Context, R>> {\n this.transformers.push(isTransformer(context) ? context : () => context)\n return this as AsyncContext<Init, LeftJoin<Context, R>>\n }\n async get<C = Context>(): Promise<C> {\n if (this.resolving) return this.resolving\n return this.resolving = this.init ? this.#buildContext() : new Promise<any>(a => this.resolve = a)\n }\n async #buildContext(): Promise<Context> {\n return this.transformers.reduce(async (p, t) => {\n const c = await p\n return { ...c, ...await t(c) as any }\n }, resolveInit(this.init!)) as Promise<Context>\n }\n}\nexport namespace AsyncContext {\n export type Initializer<Init> = () => Init | Promise<Init>\n export type Transformer<\n CurrentContext,\n AdditionalContext\n > = (context: CurrentContext) => AdditionalContext | Promise<AdditionalContext>\n}\n\nasync function resolveInit<\n Init extends Record<string | symbol, any>,\n >(init: Init | Promise<Init> | AsyncContext.Initializer<Init>): Promise<Init> {\n return isInitializer<Init>(init) ? init() : init\n}\n\nfunction isTransformer<\n CurrentContext,\n AdditionalContext\n>(context: unknown): context is AsyncContext.Transformer<CurrentContext, AdditionalContext> {\n return typeof context === 'function'\n}\n\nfunction isInitializer<T>(context: unknown): context is AsyncContext.Initializer<T> {\n return typeof context === 'function'\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { IsoError, ModuleError } from 'iso-error';
2
+ export declare class ContextAlreadyInitialized extends ModuleError {
3
+ constructor(options?: IsoError.Options);
4
+ }
5
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../ts/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEjD,qBAAa,yBAA0B,SAAQ,WAAW;gBAC5C,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO;CAGvC"}
@@ -1,7 +1,7 @@
1
1
  import { ModuleError } from 'iso-error';
2
2
  export class ContextAlreadyInitialized extends ModuleError {
3
- constructor() {
4
- super('@unional/async-context', 'context can only be initialized once.');
3
+ constructor(options) {
4
+ super('@unional/async-context', 'context can only be initialized once.', options);
5
5
  }
6
6
  }
7
7
  //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../ts/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,WAAW,EAAE,MAAM,WAAW,CAAA;AAEjD,MAAM,OAAO,yBAA0B,SAAQ,WAAW;IACxD,YAAY,OAA0B;QACpC,KAAK,CAAC,wBAAwB,EAAE,uCAAuC,EAAE,OAAO,CAAC,CAAA;IACnF,CAAC;CACF","sourcesContent":["import { IsoError, ModuleError } from 'iso-error'\n\nexport class ContextAlreadyInitialized extends ModuleError {\n constructor(options?: IsoError.Options) {\n super('@unional/async-context', 'context can only be initialized once.', options)\n }\n}\n"]}
File without changes
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA"}
File without changes
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA","sourcesContent":["export * from './AsyncContext'\nexport * from './errors'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unional/async-context",
3
- "version": "1.0.3",
3
+ "version": "3.0.0",
4
4
  "description": "Asynchronous context for functional style programming",
5
5
  "keywords": [
6
6
  "async",
@@ -23,30 +23,63 @@
23
23
  "email": "homawong@gmail.com"
24
24
  },
25
25
  "sideEffects": false,
26
- "main": "lib/index.js",
27
- "module": "libm/index.js",
28
- "jsnext:main": "libm/index.js",
29
- "typings": "libm/index.d.ts",
26
+ "main": "cjs/index.js",
27
+ "module": "esm/index.js",
28
+ "jsnext:main": "esm/index.js",
29
+ "typings": "esm/index.d.ts",
30
30
  "files": [
31
- "lib",
32
- "libm",
33
- "src"
31
+ "cjs",
32
+ "esm",
33
+ "ts",
34
+ "!**/*.spec.*"
34
35
  ],
35
- "scripts": {
36
- "build": "tsc -p tsconfig.lib.json && tsc -p tsconfig.libm.json",
37
- "clean": "rimraf lib libm .tsbuildinfo",
38
- "depcheck": "dependency-check . --unused --no-dev -i type-plus && dependency-check . --missing --no-dev",
39
- "prepublishOnly": "yarn build"
40
- },
41
36
  "dependencies": {
42
- "iso-error": "^3.2.5",
43
- "type-plus": "^1.35.1"
37
+ "iso-error": "^4.3.6",
38
+ "type-plus": "^4.11.1"
39
+ },
40
+ "devDependencies": {
41
+ "@size-limit/preset-small-lib": "^8.0.1",
42
+ "@types/node": "^16.0.0",
43
+ "@typescript-eslint/eslint-plugin": "^5.33.0",
44
+ "@typescript-eslint/parser": "^5.33.0",
45
+ "assertron": "^10.0.0",
46
+ "cross-env": "^7.0.3",
47
+ "depcheck": "^1.4.3",
48
+ "eslint": "^8.22.0",
49
+ "eslint-config-prettier": "^8.5.0",
50
+ "eslint-plugin-harmony": "^7.1.0",
51
+ "jest": "^28.1.3",
52
+ "npm-run-all": "^4.1.5",
53
+ "rimraf": "^3.0.2",
54
+ "size-limit": "^8.0.1",
55
+ "ts-jest": "^28.0.7",
56
+ "typescript": "^4.7.4"
44
57
  },
45
58
  "engines": {
46
- "node": ">= 10"
59
+ "node": ">= 14.16"
47
60
  },
48
61
  "publishConfig": {
49
62
  "access": "public"
50
63
  },
51
- "gitHead": "9a7aec02bd98f52c24a621fb3f1876a5c623e34f"
52
- }
64
+ "size-limit": [
65
+ {
66
+ "path": "./cjs/index.js"
67
+ },
68
+ {
69
+ "path": "./esm/index.js"
70
+ }
71
+ ],
72
+ "scripts": {
73
+ "build": "run-p build:cjs build:esm",
74
+ "build:cjs": "tsc -p tsconfig.cjs.json",
75
+ "build:esm": "tsc",
76
+ "clean": "rimraf cjs esm .tsbuildinfo",
77
+ "coverage": "jest --coverage",
78
+ "depcheck": "depcheck",
79
+ "lint": "cross-env TIMING=1 eslint --ext=ts,js,cjs,mjs .",
80
+ "nuke": "pnpm clean && rimraf node_modules",
81
+ "size": "size-limit",
82
+ "test": "jest",
83
+ "verify": "npm-run-all -p lint build coverage -p depcheck size"
84
+ }
85
+ }
@@ -0,0 +1,59 @@
1
+ import { LeftJoin } from 'type-plus'
2
+ import { ContextAlreadyInitialized } from './errors'
3
+
4
+ export class AsyncContext<
5
+ Init extends Record<string | symbol, any>,
6
+ Context extends Record<string | symbol, any> = Init
7
+ > {
8
+ private transformers: Array<AsyncContext.Transformer<any, any>> = []
9
+ private resolving: Promise<any> | undefined
10
+ private resolve: ((value: Promise<Context>) => void) | undefined = undefined
11
+ constructor(private init?: Init | Promise<Init> | AsyncContext.Initializer<Init>) { }
12
+ initialize<I extends Init = Init>(init: I | Promise<I> | AsyncContext.Initializer<I>): AsyncContext<I> {
13
+ if (this.init) throw new ContextAlreadyInitialized()
14
+ this.init = init
15
+
16
+ if (this.resolve) this.resolve(this.#buildContext())
17
+ return this as AsyncContext<I>
18
+ }
19
+ extend<R extends Record<string | symbol, any>>(
20
+ context: R | Promise<R> | AsyncContext.Transformer<Context, R>
21
+ ): AsyncContext<Init, LeftJoin<Context, R>> {
22
+ this.transformers.push(isTransformer(context) ? context : () => context)
23
+ return this as AsyncContext<Init, LeftJoin<Context, R>>
24
+ }
25
+ async get<C = Context>(): Promise<C> {
26
+ if (this.resolving) return this.resolving
27
+ return this.resolving = this.init ? this.#buildContext() : new Promise<any>(a => this.resolve = a)
28
+ }
29
+ async #buildContext(): Promise<Context> {
30
+ return this.transformers.reduce(async (p, t) => {
31
+ const c = await p
32
+ return { ...c, ...await t(c) as any }
33
+ }, resolveInit(this.init!)) as Promise<Context>
34
+ }
35
+ }
36
+ export namespace AsyncContext {
37
+ export type Initializer<Init> = () => Init | Promise<Init>
38
+ export type Transformer<
39
+ CurrentContext,
40
+ AdditionalContext
41
+ > = (context: CurrentContext) => AdditionalContext | Promise<AdditionalContext>
42
+ }
43
+
44
+ async function resolveInit<
45
+ Init extends Record<string | symbol, any>,
46
+ >(init: Init | Promise<Init> | AsyncContext.Initializer<Init>): Promise<Init> {
47
+ return isInitializer<Init>(init) ? init() : init
48
+ }
49
+
50
+ function isTransformer<
51
+ CurrentContext,
52
+ AdditionalContext
53
+ >(context: unknown): context is AsyncContext.Transformer<CurrentContext, AdditionalContext> {
54
+ return typeof context === 'function'
55
+ }
56
+
57
+ function isInitializer<T>(context: unknown): context is AsyncContext.Initializer<T> {
58
+ return typeof context === 'function'
59
+ }
@@ -1,7 +1,7 @@
1
- import { ModuleError } from 'iso-error'
1
+ import { IsoError, ModuleError } from 'iso-error'
2
2
 
3
3
  export class ContextAlreadyInitialized extends ModuleError {
4
- constructor() {
5
- super('@unional/async-context', 'context can only be initialized once.')
4
+ constructor(options?: IsoError.Options) {
5
+ super('@unional/async-context', 'context can only be initialized once.', options)
6
6
  }
7
7
  }
File without changes
package/CHANGELOG.md DELETED
@@ -1,24 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ## 1.0.3 (2020-09-07)
7
-
8
- **Note:** Version bump only for package @unional/async-context
9
-
10
-
11
-
12
-
13
-
14
- ## 1.0.2 (2020-03-24)
15
-
16
- **Note:** Version bump only for package @unional/async-context
17
-
18
-
19
-
20
-
21
-
22
- ## 1.0.1 (2020-03-15)
23
-
24
- **Note:** Version bump only for package @unional/async-context
@@ -1,11 +0,0 @@
1
- import { LeftJoin } from 'type-plus';
2
- export declare class AsyncContext<T extends Record<string | symbol, any>> {
3
- private context?;
4
- private resolve;
5
- private resolving;
6
- constructor(context?: T | Promise<T> | (() => T | Promise<T>) | undefined);
7
- get(): Promise<T>;
8
- initialize(context: T | Promise<T> | (() => T | Promise<T>)): void;
9
- extend<R extends Record<string | symbol, any>>(context: R | Promise<R> | ((context: AsyncContext<T>) => R | Promise<R>)): AsyncContext<LeftJoin<T, R>>;
10
- }
11
- //# sourceMappingURL=AsyncContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AsyncContext.d.ts","sourceRoot":"","sources":["../src/AsyncContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGpC,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC;IAGlD,OAAO,CAAC,OAAO,CAAC;IAF5B,OAAO,CAAC,OAAO,CAAmD;IAClE,OAAO,CAAC,SAAS,CAAwB;gBACrB,OAAO,CAAC,0BAA0B,CAAC,GAAG,QAAQ,CAAC,CAAC,aAAC;IAC/D,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;IAIvB,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAK3D,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAGvJ"}
@@ -1,93 +0,0 @@
1
- "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
- return new (P || (P = Promise))(function (resolve, reject) {
16
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
- step((generator = generator.apply(thisArg, _arguments || [])).next());
20
- });
21
- };
22
- var __generator = (this && this.__generator) || function (thisArg, body) {
23
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
- function verb(n) { return function (v) { return step([n, v]); }; }
26
- function step(op) {
27
- if (f) throw new TypeError("Generator is already executing.");
28
- while (_) try {
29
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
- if (y = 0, t) op = [op[0] & 2, t.value];
31
- switch (op[0]) {
32
- case 0: case 1: t = op; break;
33
- case 4: _.label++; return { value: op[1], done: false };
34
- case 5: _.label++; y = op[1]; op = [0]; continue;
35
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
- default:
37
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
- if (t[2]) _.ops.pop();
42
- _.trys.pop(); continue;
43
- }
44
- op = body.call(thisArg, _);
45
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
- }
48
- };
49
- Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.AsyncContext = void 0;
51
- var errors_1 = require("./errors");
52
- var AsyncContext = /** @class */ (function () {
53
- function AsyncContext(context) {
54
- this.context = context;
55
- }
56
- AsyncContext.prototype.get = function () {
57
- return __awaiter(this, void 0, void 0, function () {
58
- var _this = this;
59
- return __generator(this, function (_a) {
60
- if (this.resolving)
61
- return [2 /*return*/, this.resolving];
62
- return [2 /*return*/, this.resolving = this.context ? resolveContext(this.context) : new Promise(function (a) { return _this.resolve = a; })];
63
- });
64
- });
65
- };
66
- AsyncContext.prototype.initialize = function (context) {
67
- if (this.context)
68
- throw new errors_1.ContextAlreadyInitialized();
69
- this.context = context;
70
- if (this.resolve)
71
- this.resolve(resolveContext(context));
72
- };
73
- AsyncContext.prototype.extend = function (context) {
74
- var _this = this;
75
- return new AsyncContext(function () { return __awaiter(_this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) {
76
- switch (_c.label) {
77
- case 0:
78
- _a = [{}];
79
- return [4 /*yield*/, this.get()];
80
- case 1:
81
- _b = [__assign.apply(void 0, _a.concat([_c.sent()]))];
82
- return [4 /*yield*/, resolveContext(context, this)];
83
- case 2: return [2 /*return*/, (__assign.apply(void 0, _b.concat([_c.sent()])))];
84
- }
85
- }); }); });
86
- };
87
- return AsyncContext;
88
- }());
89
- exports.AsyncContext = AsyncContext;
90
- function resolveContext(context, currentContext) {
91
- return typeof context === 'function' ? context(currentContext) : context;
92
- }
93
- //# sourceMappingURL=AsyncContext.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AsyncContext.js","sourceRoot":"","sources":["../src/AsyncContext.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,mCAAoD;AAEpD;IAGE,sBAAoB,OAAiD;QAAjD,YAAO,GAAP,OAAO,CAA0C;IAAI,CAAC;IACpE,0BAAG,GAAT;;;;gBACE,IAAI,IAAI,CAAC,SAAS;oBAAE,sBAAO,IAAI,CAAC,SAAS,EAAA;gBACzC,sBAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAI,UAAA,CAAC,IAAI,OAAA,KAAI,CAAC,OAAO,GAAG,CAAC,EAAhB,CAAgB,CAAC,EAAA;;;KAC5G;IACD,iCAAU,GAAV,UAAW,OAAgD;QACzD,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,kCAAyB,EAAE,CAAA;QACvD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;IACzD,CAAC;IACD,6BAAM,GAAN,UAA+C,OAAwE;QAAvH,iBAEC;QADC,OAAO,IAAI,YAAY,CAAC;;;;oBAAkB,qBAAM,IAAI,CAAC,GAAG,EAAE,EAAA;;4DAAhB,SAAgB;oBAAK,qBAAM,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EAAA;wBAA9D,sBAAA,mCAA2B,SAAmC,IAAG,EAAA;;iBAAA,CAAC,CAAA;IACxG,CAAC;IACH,mBAAC;AAAD,CAAC,AAhBD,IAgBC;AAhBY,oCAAY;AAkBzB,SAAS,cAAc,CAGrB,OAAwE,EAAE,cAAgC;IAC1G,OAAO,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAE,OAAe,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;AACnF,CAAC"}
package/lib/errors.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { ModuleError } from 'iso-error';
2
- export declare class ContextAlreadyInitialized extends ModuleError {
3
- constructor();
4
- }
5
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,qBAAa,yBAA0B,SAAQ,WAAW;;CAIzD"}
package/lib/errors.js DELETED
@@ -1,26 +0,0 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- extendStatics(d, b);
11
- function __() { this.constructor = d; }
12
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
- };
14
- })();
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.ContextAlreadyInitialized = void 0;
17
- var iso_error_1 = require("iso-error");
18
- var ContextAlreadyInitialized = /** @class */ (function (_super) {
19
- __extends(ContextAlreadyInitialized, _super);
20
- function ContextAlreadyInitialized() {
21
- return _super.call(this, '@unional/async-context', 'context can only be initialized once.') || this;
22
- }
23
- return ContextAlreadyInitialized;
24
- }(iso_error_1.ModuleError));
25
- exports.ContextAlreadyInitialized = ContextAlreadyInitialized;
26
- //# sourceMappingURL=errors.js.map
package/lib/errors.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uCAAuC;AAEvC;IAA+C,6CAAW;IACxD;eACE,kBAAM,wBAAwB,EAAE,uCAAuC,CAAC;IAC1E,CAAC;IACH,gCAAC;AAAD,CAAC,AAJD,CAA+C,uBAAW,GAIzD;AAJY,8DAAyB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA"}
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iDAA8B;AAC9B,2CAAwB"}
@@ -1,11 +0,0 @@
1
- import { LeftJoin } from 'type-plus';
2
- export declare class AsyncContext<T extends Record<string | symbol, any>> {
3
- private context?;
4
- private resolve;
5
- private resolving;
6
- constructor(context?: T | Promise<T> | (() => T | Promise<T>) | undefined);
7
- get(): Promise<T>;
8
- initialize(context: T | Promise<T> | (() => T | Promise<T>)): void;
9
- extend<R extends Record<string | symbol, any>>(context: R | Promise<R> | ((context: AsyncContext<T>) => R | Promise<R>)): AsyncContext<LeftJoin<T, R>>;
10
- }
11
- //# sourceMappingURL=AsyncContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AsyncContext.d.ts","sourceRoot":"","sources":["../src/AsyncContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAGpC,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC;IAGlD,OAAO,CAAC,OAAO,CAAC;IAF5B,OAAO,CAAC,OAAO,CAAmD;IAClE,OAAO,CAAC,SAAS,CAAwB;gBACrB,OAAO,CAAC,0BAA0B,CAAC,GAAG,QAAQ,CAAC,CAAC,aAAC;IAC/D,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;IAIvB,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAK3D,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAGvJ"}
@@ -1,25 +0,0 @@
1
- import { ContextAlreadyInitialized } from './errors';
2
- export class AsyncContext {
3
- constructor(context) {
4
- this.context = context;
5
- }
6
- async get() {
7
- if (this.resolving)
8
- return this.resolving;
9
- return this.resolving = this.context ? resolveContext(this.context) : new Promise(a => this.resolve = a);
10
- }
11
- initialize(context) {
12
- if (this.context)
13
- throw new ContextAlreadyInitialized();
14
- this.context = context;
15
- if (this.resolve)
16
- this.resolve(resolveContext(context));
17
- }
18
- extend(context) {
19
- return new AsyncContext(async () => (Object.assign(Object.assign({}, await this.get()), await resolveContext(context, this))));
20
- }
21
- }
22
- function resolveContext(context, currentContext) {
23
- return typeof context === 'function' ? context(currentContext) : context;
24
- }
25
- //# sourceMappingURL=AsyncContext.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AsyncContext.js","sourceRoot":"","sources":["../src/AsyncContext.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAEpD,MAAM,OAAO,YAAY;IAGvB,YAAoB,OAAiD;QAAjD,YAAO,GAAP,OAAO,CAA0C;IAAI,CAAC;IAC1E,KAAK,CAAC,GAAG;QACP,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAA;QACzC,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;IAC7G,CAAC;IACD,UAAU,CAAC,OAAgD;QACzD,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,yBAAyB,EAAE,CAAA;QACvD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAA;IACzD,CAAC;IACD,MAAM,CAAyC,OAAwE;QACrH,OAAO,IAAI,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,iCAAM,MAAM,IAAI,CAAC,GAAG,EAAE,GAAK,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EAAG,CAAC,CAAA;IACxG,CAAC;CACF;AAED,SAAS,cAAc,CAGrB,OAAwE,EAAE,cAAgC;IAC1G,OAAO,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAE,OAAe,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;AACnF,CAAC"}
package/libm/errors.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { ModuleError } from 'iso-error';
2
- export declare class ContextAlreadyInitialized extends ModuleError {
3
- constructor();
4
- }
5
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,qBAAa,yBAA0B,SAAQ,WAAW;;CAIzD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,MAAM,OAAO,yBAA0B,SAAQ,WAAW;IACxD;QACE,KAAK,CAAC,wBAAwB,EAAE,uCAAuC,CAAC,CAAA;IAC1E,CAAC;CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA"}
package/libm/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA"}
@@ -1,187 +0,0 @@
1
- import a, { AssertOrder } from 'assertron'
2
- import { assertType, assignability } from 'type-plus'
3
- import { AsyncContext, ContextAlreadyInitialized } from '.'
4
-
5
- describe('constructor', () => {
6
- test('with object', async () => {
7
- const ctx = new AsyncContext({ a: 1 })
8
-
9
- const { a } = await ctx.get()
10
-
11
- expect(a).toBe(1)
12
- })
13
-
14
- test('with Promise', async () => {
15
- const ctx = new AsyncContext(Promise.resolve({ a: 1 }))
16
-
17
- const { a } = await ctx.get()
18
-
19
- expect(a).toBe(1)
20
- })
21
-
22
- test('with initialize function', async () => {
23
- const ctx = new AsyncContext(() => ({ a: 1 }))
24
- const { a } = await ctx.get()
25
-
26
- expect(a).toBe(1)
27
- })
28
-
29
- test('with async initialize function', async () => {
30
- const ctx = new AsyncContext(async () => ({ a: 1 }))
31
- const { a } = await ctx.get()
32
-
33
- expect(a).toBe(1)
34
- })
35
-
36
- test('constructor initialize function is only called once', async () => {
37
- const o = new AssertOrder(1)
38
- const ctx = new AsyncContext(() => {
39
- o.once(1)
40
- return { a: 1 }
41
- })
42
- await ctx.get()
43
- const { a } = await ctx.get()
44
-
45
- expect(a).toBe(1)
46
- o.end()
47
- })
48
-
49
- test('intialize function is not called until first get()', async () => {
50
- new AsyncContext(() => { throw new Error('should not reach') })
51
- })
52
- })
53
-
54
- describe('initialize()', () => {
55
- test('specify type through generics', async () => {
56
- const ctx = new AsyncContext<{ a: string }>()
57
- ctx.initialize({ a: 'a' })
58
- const { a } = await ctx.get()
59
-
60
- assertType.isTrue(assignability<string>()(a))
61
- expect(a).toBe('a')
62
- })
63
-
64
- test('with object', async () => {
65
- const ctx = new AsyncContext()
66
-
67
- ctx.initialize({ a: 1 })
68
- const { a } = await ctx.get()
69
-
70
- expect(a).toBe(1)
71
- })
72
-
73
- test('with promise', async () => {
74
- const ctx = new AsyncContext()
75
-
76
- ctx.initialize(Promise.resolve({ a: 1 }))
77
- const { a } = await ctx.get()
78
-
79
- expect(a).toBe(1)
80
- })
81
-
82
- test('initialize function', async () => {
83
- const ctx = new AsyncContext()
84
-
85
- ctx.initialize(() => ({ a: 1 }))
86
- const { a } = await ctx.get()
87
-
88
- expect(a).toBe(1)
89
- })
90
-
91
- test('with async initialize function', async () => {
92
- const ctx = new AsyncContext()
93
-
94
- ctx.initialize(async () => ({ a: 1 }))
95
- const { a } = await ctx.get()
96
-
97
- expect(a).toBe(1)
98
- })
99
-
100
- test('initialize function is only called once', async () => {
101
- const o = new AssertOrder(1)
102
- const ctx = new AsyncContext()
103
- ctx.initialize(() => {
104
- o.once(1)
105
- return { a: 1 }
106
- })
107
- await ctx.get()
108
- const { a } = await ctx.get()
109
-
110
- expect(a).toBe(1)
111
- o.end()
112
- })
113
-
114
- test('call initialize function with constructor initialized context throws ContextAlreadyInitialized', () => {
115
- const ctx = new AsyncContext({ a: 1 })
116
-
117
- a.throws(() => ctx.initialize({ a: 2 }), ContextAlreadyInitialized)
118
- })
119
-
120
- test('intialize function is not called until first get()', async () => {
121
- const ctx = new AsyncContext()
122
- ctx.initialize(() => { throw new Error('should not reach') })
123
- })
124
- })
125
-
126
- describe('extend()', () => {
127
- test('with object', async () => {
128
- const ctx = new AsyncContext({ a: 1 })
129
- const actual = ctx.extend({ b: 'b' })
130
-
131
- expect(await actual.get()).toEqual({ a: 1, b: 'b' })
132
- })
133
-
134
- test('with promise', async () => {
135
- const ctx = new AsyncContext({ a: 1 })
136
- const actual = ctx.extend(Promise.resolve({ b: 'b' }))
137
-
138
- expect(await actual.get()).toEqual({ a: 1, b: 'b' })
139
- })
140
-
141
- test('with function', async () => {
142
- const ctx = new AsyncContext({ a: 1 })
143
- const actual = ctx.extend(() => ({ b: 'b' }))
144
-
145
- expect(await actual.get()).toEqual({ a: 1, b: 'b' })
146
- })
147
-
148
- test('with async function', async () => {
149
- const ctx = new AsyncContext({ a: 1 })
150
- const actual = ctx.extend(async () => ({ b: 'b' }))
151
-
152
- expect(await actual.get()).toEqual({ a: 1, b: 'b' })
153
- })
154
-
155
- test('overrides existing value', async () => {
156
- const ctx = new AsyncContext({ a: 1 }).extend(() => ({ a: 2 }))
157
- expect(await ctx.get()).toEqual({ a: 2 })
158
- })
159
-
160
- test('change types of existing property', async () => {
161
- type Orig = { type: 'a' | 'b', value: string }
162
- const ctx = new AsyncContext<Orig>({ type: 'a', value: '1' })
163
- .extend(() => ({ value: 1 }))
164
-
165
- const result = await ctx.get()
166
-
167
- assertType<'a' | 'b'>(result.type)
168
- assertType.isNumber(result.value)
169
- })
170
-
171
- test('handler receives current context for more better merge function reuse', async () => {
172
- type Orig = { type: 'a' | 'b', value: number }
173
-
174
- const orig = new AsyncContext<Orig>({ type: 'a', value: 1 })
175
- const transform = async (context: AsyncContext<Orig>) => ({ value: String(await (await context.get()).value) })
176
-
177
- const merged = orig.extend(transform)
178
- expect(await merged.get()).toEqual({ type: 'a', value: '1' })
179
- })
180
- })
181
-
182
- test('get() waits for initialize() with no initial context', async () => {
183
- const ctx = new AsyncContext()
184
- setImmediate(() => ctx.initialize({ a: 2 }))
185
- const { a } = await ctx.get()
186
- expect(a).toBe(2)
187
- })
@@ -1,27 +0,0 @@
1
- import { LeftJoin } from 'type-plus'
2
- import { ContextAlreadyInitialized } from './errors'
3
-
4
- export class AsyncContext<T extends Record<string | symbol, any>> {
5
- private resolve: ((value: T | PromiseLike<T>) => void) | undefined
6
- private resolving: Promise<T> | undefined
7
- constructor(private context?: T | Promise<T> | (() => T | Promise<T>)) { }
8
- async get(): Promise<T> {
9
- if (this.resolving) return this.resolving
10
- return this.resolving = this.context ? resolveContext(this.context) : new Promise<T>(a => this.resolve = a)
11
- }
12
- initialize(context: T | Promise<T> | (() => T | Promise<T>)) {
13
- if (this.context) throw new ContextAlreadyInitialized()
14
- this.context = context
15
- if (this.resolve) this.resolve(resolveContext(context))
16
- }
17
- extend<R extends Record<string | symbol, any>>(context: R | Promise<R> | ((context: AsyncContext<T>) => R | Promise<R>)): AsyncContext<LeftJoin<T, R>> {
18
- return new AsyncContext(async () => ({ ...await this.get(), ...await resolveContext(context, this) }))
19
- }
20
- }
21
-
22
- function resolveContext<
23
- T extends Record<string | symbol, any>,
24
- R extends Record<string | symbol, any>
25
- >(context: R | Promise<R> | ((context: AsyncContext<T>) => R | Promise<R>), currentContext?: AsyncContext<T>) {
26
- return typeof context === 'function' ? (context as any)(currentContext) : context
27
- }