conode 0.1.23 → 0.2.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 +26 -15
- package/build/index.cjs +54 -59
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +13 -12
- package/build/index.js +33 -40
- package/build/index.js.map +1 -1
- package/package.json +13 -14
- package/src/index.ts +34 -45
package/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# CoNode
|
|
2
2
|
|
|
3
|
-
0-
|
|
3
|
+
0-deps async context management for Node.js
|
|
4
4
|
|
|
5
5
|
[![Build Status][github-image]][github-url]
|
|
6
6
|
[![NPM version][npm-image]][npm-url]
|
|
7
7
|
[![Downloads][downloads-image]][npm-url]
|
|
8
8
|
[![Coverage Status][codecov-image]][codecov-url]
|
|
9
|
-
[![Maintainability][codeclimate-image]][codeclimate-url]
|
|
10
|
-
[![Snyk][snyk-image]][snyk-url]
|
|
11
9
|
|
|
12
10
|
## Table of Contents
|
|
13
11
|
|
|
@@ -18,9 +16,10 @@
|
|
|
18
16
|
|
|
19
17
|
## Features
|
|
20
18
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
19
|
+
- Zero dependencies
|
|
20
|
+
- Lightweight wrappers around [AsyncLocalStorage](https://nodejs.org/api/async_hooks.html)
|
|
21
|
+
- Full TypeScript support
|
|
22
|
+
- Works in CommonJS and ES Module environments
|
|
24
23
|
|
|
25
24
|
## Installing
|
|
26
25
|
|
|
@@ -38,6 +37,22 @@ yarn add conode
|
|
|
38
37
|
|
|
39
38
|
## Examples
|
|
40
39
|
|
|
40
|
+
Quick start
|
|
41
|
+
```typescript
|
|
42
|
+
import { createContext } from 'conode';
|
|
43
|
+
|
|
44
|
+
interface RequestContext { userId: string }
|
|
45
|
+
|
|
46
|
+
const reqCtx = createContext<RequestContext>();
|
|
47
|
+
|
|
48
|
+
async function handleRequest() {
|
|
49
|
+
console.log(reqCtx.getContext()?.userId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await reqCtx.contextualize({ userId: 'abc123' }, handleRequest);
|
|
53
|
+
// Logs: 'abc123'
|
|
54
|
+
```
|
|
55
|
+
|
|
41
56
|
Functional approach
|
|
42
57
|
```typescript
|
|
43
58
|
import { createContext } from '../index';
|
|
@@ -51,16 +66,16 @@ const action = () => {
|
|
|
51
66
|
auth.contextualize(jwt, action);
|
|
52
67
|
```
|
|
53
68
|
|
|
54
|
-
Class approach
|
|
69
|
+
Class approach with decorator
|
|
55
70
|
```typescript
|
|
56
|
-
import { contextualize,
|
|
71
|
+
import { contextualize, Contextable } from '../index';
|
|
57
72
|
|
|
58
73
|
const action = () => {
|
|
59
74
|
const service = Service.getContext();
|
|
60
75
|
console.log(service.getSomething());
|
|
61
76
|
};
|
|
62
77
|
|
|
63
|
-
class Service extends
|
|
78
|
+
class Service extends Contextable() {
|
|
64
79
|
@contextualize
|
|
65
80
|
async run() {
|
|
66
81
|
await action();
|
|
@@ -83,12 +98,8 @@ Copyright (c) 2022-present Ivan Zakharchanka
|
|
|
83
98
|
[npm-url]: https://www.npmjs.com/package/conode
|
|
84
99
|
[downloads-image]: https://img.shields.io/npm/dw/conode.svg?maxAge=43200
|
|
85
100
|
[npm-image]: https://img.shields.io/npm/v/conode.svg?maxAge=43200
|
|
86
|
-
[github-url]: https://github.com/3axap4eHko/conode/actions/workflows/
|
|
87
|
-
[github-image]: https://github.com/3axap4eHko/conode/actions/workflows/
|
|
101
|
+
[github-url]: https://github.com/3axap4eHko/conode/actions/workflows/build.yml
|
|
102
|
+
[github-image]: https://github.com/3axap4eHko/conode/actions/workflows/build.yml/badge.svg
|
|
88
103
|
[codecov-url]: https://codecov.io/gh/3axap4eHko/conode
|
|
89
104
|
[codecov-image]: https://codecov.io/gh/3axap4eHko/conode/branch/master/graph/badge.svg?token=JZ8QCGH6PI
|
|
90
|
-
[codeclimate-url]: https://codeclimate.com/github/3axap4eHko/conode/maintainability
|
|
91
|
-
[codeclimate-image]: https://api.codeclimate.com/v1/badges/0ba20f27f6db2b0fec8c/maintainability
|
|
92
|
-
[snyk-url]: https://snyk.io/test/npm/conode/latest
|
|
93
|
-
[snyk-image]: https://img.shields.io/snyk/vulnerabilities/github/3axap4eHko/conode.svg?maxAge=43200
|
|
94
105
|
|
package/build/index.cjs
CHANGED
|
@@ -1,70 +1,65 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
9
|
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
Contextable: function() {
|
|
13
|
+
return Contextable;
|
|
14
|
+
},
|
|
15
|
+
contextualize: function() {
|
|
16
|
+
return contextualize;
|
|
17
|
+
},
|
|
18
|
+
createContext: function() {
|
|
19
|
+
return createContext;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const _nodeasync_hooks = require("node:async_hooks");
|
|
23
|
+
const createContext = ()=>{
|
|
24
|
+
const storage = new _nodeasync_hooks.AsyncLocalStorage();
|
|
16
25
|
return {
|
|
17
|
-
contextualize(
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
yield callback();
|
|
22
|
-
resolve();
|
|
23
|
-
}
|
|
24
|
-
catch (e) {
|
|
25
|
-
reject(e);
|
|
26
|
-
}
|
|
27
|
-
}));
|
|
28
|
-
});
|
|
26
|
+
contextualize (context, callback) {
|
|
27
|
+
return storage.run({
|
|
28
|
+
context
|
|
29
|
+
}, callback);
|
|
29
30
|
},
|
|
30
|
-
getContext() {
|
|
31
|
-
|
|
31
|
+
getContext () {
|
|
32
|
+
const store = storage.getStore();
|
|
33
|
+
if (!store) {
|
|
34
|
+
throw new Error('Context not initialized');
|
|
35
|
+
}
|
|
36
|
+
return store.context;
|
|
32
37
|
}
|
|
33
38
|
};
|
|
34
39
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const context =
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return context.getContext();
|
|
42
|
-
}
|
|
43
|
-
contextualize(callback) {
|
|
44
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
-
return context.contextualize(this, callback);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
40
|
+
function Contextable(BaseClass = class {
|
|
41
|
+
}) {
|
|
42
|
+
const context = createContext();
|
|
43
|
+
return class Contextual extends BaseClass {
|
|
44
|
+
static getContext() {
|
|
45
|
+
return context.getContext();
|
|
48
46
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
contextualize(callback)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
};
|
|
47
|
+
constructor(...args){
|
|
48
|
+
super(...args);
|
|
49
|
+
}
|
|
50
|
+
contextualize(callback) {
|
|
51
|
+
return context.contextualize(this, callback);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
59
54
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
function contextualize(...args) {
|
|
56
|
+
const [, , descriptor] = args;
|
|
57
|
+
const property = descriptor?.value;
|
|
58
|
+
if (typeof property === 'function') {
|
|
59
|
+
descriptor.value = function(...args) {
|
|
60
|
+
return this.contextualize(()=>property.apply(this, args));
|
|
61
|
+
};
|
|
65
62
|
}
|
|
66
|
-
;
|
|
67
|
-
descriptor.value = contextualized;
|
|
68
63
|
}
|
|
69
|
-
|
|
70
|
-
//# sourceMappingURL=index.cjs.map
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=index.cjs.map
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\n\nexport type Fn<T extends unknown[], R> = (...args: T) => R;\n\nexport const createContext = <T>() => {\n const storage = new AsyncLocalStorage<{ context: T }>();\n return {\n contextualize<R>(context: T, callback: Fn<[], R>): R {\n return storage.run({ context }, callback);\n },\n getContext() {\n const store = storage.getStore();\n if (!store) {\n throw new Error('Context not initialized');\n }\n return store.context;\n }\n };\n};\n\ntype Constructor = new (...args: any) => any;\n\nexport interface Contextualized {\n contextualize<R>(callback: Fn<[], R>): Promise<R>;\n}\n\nexport function Contextable(BaseClass: Constructor = class { }) {\n const context = createContext();\n return class Contextual extends BaseClass {\n static getContext<C extends Constructor>(this: C) {\n return context.getContext() as InstanceType<C>;\n }\n constructor(...args: unknown[]) {\n super(...args);\n }\n\n contextualize<R>(callback: Fn<[], R>): R {\n return context.contextualize(this, callback);\n }\n };\n}\n\nexport function contextualize<T extends Contextualized, K extends keyof T>(...args: unknown[]) {\n const [, , descriptor] = args as [T, K, TypedPropertyDescriptor<T[K]>];\n const property = descriptor?.value;\n if (typeof property === 'function') {\n descriptor.value = function(this: Contextualized, ...args: unknown[]) {\n return this.contextualize(() => property.apply(this, args));\n } as T[K];\n }\n}\n\n"],"names":["Contextable","contextualize","createContext","storage","AsyncLocalStorage","context","callback","run","getContext","store","getStore","Error","BaseClass","Contextual","constructor","args","descriptor","property","value","apply"],"mappings":";;;;;;;;;;;IA0BgBA,WAAW;eAAXA;;IAgBAC,aAAa;eAAbA;;IAtCHC,aAAa;eAAbA;;;iCAJqB;AAI3B,MAAMA,gBAAgB;IAC3B,MAAMC,UAAU,IAAIC,kCAAiB;IACrC,OAAO;QACLH,eAAiBI,OAAU,EAAEC,QAAmB;YAC9C,OAAOH,QAAQI,GAAG,CAAC;gBAAEF;YAAQ,GAAGC;QAClC;QACAE;YACE,MAAMC,QAAQN,QAAQO,QAAQ;YAC9B,IAAI,CAACD,OAAO;gBACV,MAAM,IAAIE,MAAM;YAClB;YACA,OAAOF,MAAMJ,OAAO;QACtB;IACF;AACF;AAQO,SAASL,YAAYY,YAAyB;AAAQ,CAAC;IAC5D,MAAMP,UAAUH;IAChB,OAAO,MAAMW,mBAAmBD;QAC9B,OAAOJ,aAA2C;YAChD,OAAOH,QAAQG,UAAU;QAC3B;QACAM,YAAY,GAAGC,IAAe,CAAE;YAC9B,KAAK,IAAIA;QACX;QAEAd,cAAiBK,QAAmB,EAAK;YACvC,OAAOD,QAAQJ,aAAa,CAAC,IAAI,EAAEK;QACrC;IACF;AACF;AAEO,SAASL,cAA2D,GAAGc,IAAe;IAC3F,MAAM,KAAKC,WAAW,GAAGD;IACzB,MAAME,WAAWD,YAAYE;IAC7B,IAAI,OAAOD,aAAa,YAAY;QAClCD,WAAWE,KAAK,GAAG,SAA+B,GAAGH,IAAe;YAClE,OAAO,IAAI,CAACd,aAAa,CAAC,IAAMgB,SAASE,KAAK,CAAC,IAAI,EAAEJ;QACvD;IACF;AACF"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type Fn<T extends unknown[], R> = (...args: T) => R;
|
|
2
2
|
export declare const createContext: <T>() => {
|
|
3
|
-
contextualize(
|
|
4
|
-
getContext(): T
|
|
5
|
-
};
|
|
6
|
-
type Constructor = new (...args: any[]) => {};
|
|
7
|
-
export declare function Context(Base?: Constructor): {
|
|
8
|
-
new (...args: any[]): {
|
|
9
|
-
contextualize(callback: Callback): Promise<void>;
|
|
10
|
-
};
|
|
11
|
-
readonly context: unknown;
|
|
3
|
+
contextualize<R>(context: T, callback: Fn<[], R>): R;
|
|
4
|
+
getContext(): T;
|
|
12
5
|
};
|
|
6
|
+
type Constructor = new (...args: any) => any;
|
|
13
7
|
export interface Contextualized {
|
|
14
|
-
contextualize(callback:
|
|
8
|
+
contextualize<R>(callback: Fn<[], R>): Promise<R>;
|
|
15
9
|
}
|
|
16
|
-
export declare function
|
|
10
|
+
export declare function Contextable(BaseClass?: Constructor): {
|
|
11
|
+
new (...args: unknown[]): {
|
|
12
|
+
[x: string]: any;
|
|
13
|
+
contextualize<R>(callback: Fn<[], R>): R;
|
|
14
|
+
};
|
|
15
|
+
getContext<C extends Constructor>(this: C): InstanceType<C>;
|
|
16
|
+
};
|
|
17
|
+
export declare function contextualize<T extends Contextualized, K extends keyof T>(...args: unknown[]): void;
|
|
17
18
|
export {};
|
package/build/index.js
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
-
export const createContext = ()
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
export const createContext = ()=>{
|
|
3
3
|
const storage = new AsyncLocalStorage();
|
|
4
4
|
return {
|
|
5
|
-
contextualize(
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
await callback();
|
|
10
|
-
resolve();
|
|
11
|
-
}
|
|
12
|
-
catch (e) {
|
|
13
|
-
reject(e);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
});
|
|
5
|
+
contextualize (context, callback) {
|
|
6
|
+
return storage.run({
|
|
7
|
+
context
|
|
8
|
+
}, callback);
|
|
17
9
|
},
|
|
18
|
-
getContext() {
|
|
19
|
-
|
|
10
|
+
getContext () {
|
|
11
|
+
const store = storage.getStore();
|
|
12
|
+
if (!store) {
|
|
13
|
+
throw new Error('Context not initialized');
|
|
14
|
+
}
|
|
15
|
+
return store.context;
|
|
20
16
|
}
|
|
21
17
|
};
|
|
22
18
|
};
|
|
23
|
-
export function
|
|
19
|
+
export function Contextable(BaseClass = class {
|
|
20
|
+
}) {
|
|
24
21
|
const context = createContext();
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return context.getContext();
|
|
29
|
-
}
|
|
30
|
-
async contextualize(callback) {
|
|
31
|
-
return context.contextualize(this, callback);
|
|
32
|
-
}
|
|
22
|
+
return class Contextual extends BaseClass {
|
|
23
|
+
static getContext() {
|
|
24
|
+
return context.getContext();
|
|
33
25
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
26
|
+
constructor(...args){
|
|
27
|
+
super(...args);
|
|
28
|
+
}
|
|
29
|
+
contextualize(callback) {
|
|
30
|
+
return context.contextualize(this, callback);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
42
33
|
}
|
|
43
|
-
export function contextualize(
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
export function contextualize(...args) {
|
|
35
|
+
const [, , descriptor] = args;
|
|
36
|
+
const property = descriptor?.value;
|
|
37
|
+
if (typeof property === 'function') {
|
|
38
|
+
descriptor.value = function(...args) {
|
|
39
|
+
return this.contextualize(()=>property.apply(this, args));
|
|
40
|
+
};
|
|
47
41
|
}
|
|
48
|
-
;
|
|
49
|
-
descriptor.value = contextualized;
|
|
50
42
|
}
|
|
51
|
-
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\n\nexport type Fn<T extends unknown[], R> = (...args: T) => R;\n\nexport const createContext = <T>() => {\n const storage = new AsyncLocalStorage<{ context: T }>();\n return {\n contextualize<R>(context: T, callback: Fn<[], R>): R {\n return storage.run({ context }, callback);\n },\n getContext() {\n const store = storage.getStore();\n if (!store) {\n throw new Error('Context not initialized');\n }\n return store.context;\n }\n };\n};\n\ntype Constructor = new (...args: any) => any;\n\nexport interface Contextualized {\n contextualize<R>(callback: Fn<[], R>): Promise<R>;\n}\n\nexport function Contextable(BaseClass: Constructor = class { }) {\n const context = createContext();\n return class Contextual extends BaseClass {\n static getContext<C extends Constructor>(this: C) {\n return context.getContext() as InstanceType<C>;\n }\n constructor(...args: unknown[]) {\n super(...args);\n }\n\n contextualize<R>(callback: Fn<[], R>): R {\n return context.contextualize(this, callback);\n }\n };\n}\n\nexport function contextualize<T extends Contextualized, K extends keyof T>(...args: unknown[]) {\n const [, , descriptor] = args as [T, K, TypedPropertyDescriptor<T[K]>];\n const property = descriptor?.value;\n if (typeof property === 'function') {\n descriptor.value = function(this: Contextualized, ...args: unknown[]) {\n return this.contextualize(() => property.apply(this, args));\n } as T[K];\n }\n}\n\n"],"names":["AsyncLocalStorage","createContext","storage","contextualize","context","callback","run","getContext","store","getStore","Error","Contextable","BaseClass","Contextual","constructor","args","descriptor","property","value","apply"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,mBAAmB;AAIrD,OAAO,MAAMC,gBAAgB;IAC3B,MAAMC,UAAU,IAAIF;IACpB,OAAO;QACLG,eAAiBC,OAAU,EAAEC,QAAmB;YAC9C,OAAOH,QAAQI,GAAG,CAAC;gBAAEF;YAAQ,GAAGC;QAClC;QACAE;YACE,MAAMC,QAAQN,QAAQO,QAAQ;YAC9B,IAAI,CAACD,OAAO;gBACV,MAAM,IAAIE,MAAM;YAClB;YACA,OAAOF,MAAMJ,OAAO;QACtB;IACF;AACF,EAAE;AAQF,OAAO,SAASO,YAAYC,YAAyB;AAAQ,CAAC;IAC5D,MAAMR,UAAUH;IAChB,OAAO,MAAMY,mBAAmBD;QAC9B,OAAOL,aAA2C;YAChD,OAAOH,QAAQG,UAAU;QAC3B;QACAO,YAAY,GAAGC,IAAe,CAAE;YAC9B,KAAK,IAAIA;QACX;QAEAZ,cAAiBE,QAAmB,EAAK;YACvC,OAAOD,QAAQD,aAAa,CAAC,IAAI,EAAEE;QACrC;IACF;AACF;AAEA,OAAO,SAASF,cAA2D,GAAGY,IAAe;IAC3F,MAAM,KAAKC,WAAW,GAAGD;IACzB,MAAME,WAAWD,YAAYE;IAC7B,IAAI,OAAOD,aAAa,YAAY;QAClCD,WAAWE,KAAK,GAAG,SAA+B,GAAGH,IAAe;YAClE,OAAO,IAAI,CAACZ,aAAa,CAAC,IAAMc,SAASE,KAAK,CAAC,IAAI,EAAEJ;QACvD;IACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "0-Deps, simple and fast context library for NodeJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.cjs",
|
|
@@ -11,13 +11,6 @@
|
|
|
11
11
|
],
|
|
12
12
|
"module": "build/index.js",
|
|
13
13
|
"sideEffects": false,
|
|
14
|
-
"scripts": {
|
|
15
|
-
"convert-cjs": "node scripts/convert-cjs.js",
|
|
16
|
-
"build": "rm -rf build && yarn build:cjs && yarn build:mjs",
|
|
17
|
-
"build:cjs": "tsc -p tsconfig.cjs.json && yarn convert-cjs",
|
|
18
|
-
"build:mjs": "tsc -p tsconfig.mjs.json --declaration",
|
|
19
|
-
"test": "jest"
|
|
20
|
-
},
|
|
21
14
|
"repository": {
|
|
22
15
|
"type": "git",
|
|
23
16
|
"url": "git+https://github.com/3axap4eHko/conode.git"
|
|
@@ -39,10 +32,16 @@
|
|
|
39
32
|
},
|
|
40
33
|
"homepage": "https://github.com/3axap4eHko/conode#readme",
|
|
41
34
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@types/
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
35
|
+
"@swc/jest": "^0.2.38",
|
|
36
|
+
"@types/jest": "^29.5.14",
|
|
37
|
+
"@types/node": "^22.15.18",
|
|
38
|
+
"inop": "^0.7.8",
|
|
39
|
+
"jest": "^29.7.0",
|
|
40
|
+
"typescript": "^5.8.3"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
44
|
+
"test": "jest",
|
|
45
|
+
"test:build": "node src/__tests__/index.cjs && node src/__tests__/index.mjs"
|
|
47
46
|
}
|
|
48
|
-
}
|
|
47
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,63 +1,52 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'async_hooks';
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type Fn<T extends unknown[], R> = (...args: T) => R;
|
|
4
4
|
|
|
5
5
|
export const createContext = <T>() => {
|
|
6
|
-
const storage = new AsyncLocalStorage<T>();
|
|
7
|
-
|
|
6
|
+
const storage = new AsyncLocalStorage<{ context: T }>();
|
|
8
7
|
return {
|
|
9
|
-
contextualize(
|
|
10
|
-
return
|
|
11
|
-
storage.run(value, async () => {
|
|
12
|
-
try {
|
|
13
|
-
await callback();
|
|
14
|
-
resolve();
|
|
15
|
-
} catch (e) {
|
|
16
|
-
reject(e);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
});
|
|
8
|
+
contextualize<R>(context: T, callback: Fn<[], R>): R {
|
|
9
|
+
return storage.run({ context }, callback);
|
|
20
10
|
},
|
|
21
11
|
getContext() {
|
|
22
|
-
|
|
12
|
+
const store = storage.getStore();
|
|
13
|
+
if (!store) {
|
|
14
|
+
throw new Error('Context not initialized');
|
|
15
|
+
}
|
|
16
|
+
return store.context;
|
|
23
17
|
}
|
|
24
18
|
};
|
|
25
19
|
};
|
|
26
20
|
|
|
27
|
-
type Constructor = new (...args: any
|
|
21
|
+
type Constructor = new (...args: any) => any;
|
|
28
22
|
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return Base
|
|
33
|
-
? class Contextual extends Base {
|
|
34
|
-
static get context() {
|
|
35
|
-
return context.getContext();
|
|
36
|
-
}
|
|
23
|
+
export interface Contextualized {
|
|
24
|
+
contextualize<R>(callback: Fn<[], R>): Promise<R>;
|
|
25
|
+
}
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
export function Contextable(BaseClass: Constructor = class { }) {
|
|
28
|
+
const context = createContext();
|
|
29
|
+
return class Contextual extends BaseClass {
|
|
30
|
+
static getContext<C extends Constructor>(this: C) {
|
|
31
|
+
return context.getContext() as InstanceType<C>;
|
|
32
|
+
}
|
|
33
|
+
constructor(...args: unknown[]) {
|
|
34
|
+
super(...args);
|
|
41
35
|
}
|
|
42
|
-
: class Contextual {
|
|
43
|
-
static get context() {
|
|
44
|
-
return context.getContext();
|
|
45
|
-
}
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
37
|
+
contextualize<R>(callback: Fn<[], R>): R {
|
|
38
|
+
return context.contextualize(this, callback);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
export
|
|
54
|
-
|
|
43
|
+
export function contextualize<T extends Contextualized, K extends keyof T>(...args: unknown[]) {
|
|
44
|
+
const [, , descriptor] = args as [T, K, TypedPropertyDescriptor<T[K]>];
|
|
45
|
+
const property = descriptor?.value;
|
|
46
|
+
if (typeof property === 'function') {
|
|
47
|
+
descriptor.value = function(this: Contextualized, ...args: unknown[]) {
|
|
48
|
+
return this.contextualize(() => property.apply(this, args));
|
|
49
|
+
} as T[K];
|
|
50
|
+
}
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
export function contextualize<T extends Contextualized>(target: T, _: string, descriptor: TypedPropertyDescriptor<Callback>) {
|
|
58
|
-
const method = descriptor.value!;
|
|
59
|
-
function contextualized() {
|
|
60
|
-
return target.contextualize(() => method.apply(this, arguments));
|
|
61
|
-
};
|
|
62
|
-
descriptor.value = contextualized as any;
|
|
63
|
-
}
|