siringa 0.0.2 → 0.0.5
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 +40 -0
- package/dist/index.js +42 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -10
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +27 -7
- package/package.json +7 -7
- package/src/index.ts +85 -15
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ decorator metadata.
|
|
|
10
10
|
* [Quick Start](#quick-start)
|
|
11
11
|
* [One-Time Injection](#one-time-injection)
|
|
12
12
|
* [Name-based Injection](#name-based-injection)
|
|
13
|
+
* [Promises Injection](#promises-injection)
|
|
13
14
|
* [Using Factories](#using-factories)
|
|
14
15
|
* [Using Instances](#using-instances)
|
|
15
16
|
* [Child Injectors](#child-injectors)
|
|
@@ -116,6 +117,40 @@ const bar = await injector.get('bar')
|
|
|
116
117
|
const foo = await injector.get('foo')
|
|
117
118
|
```
|
|
118
119
|
|
|
120
|
+
Promises Injection
|
|
121
|
+
------------------
|
|
122
|
+
|
|
123
|
+
Sometimes it might be necessary to be injected a `Promise` of a component,
|
|
124
|
+
rather than a component itself.
|
|
125
|
+
|
|
126
|
+
For example, in an _Lambda Function_ we might want to asynchronously use other
|
|
127
|
+
services (retrieve secrets, connect to databases, ...) while starting to process
|
|
128
|
+
a request.
|
|
129
|
+
|
|
130
|
+
In this case we can request to be injected a (yet unresolved) `Promise` of a
|
|
131
|
+
bound component:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { promise } from 'siringa'
|
|
135
|
+
|
|
136
|
+
class Foo {
|
|
137
|
+
foo() { /* ... your code... */ }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class Bar {
|
|
141
|
+
// The `promise(...)` function declares the injection of a `Promise`
|
|
142
|
+
static $inject = [ promise(Foo) ] as const
|
|
143
|
+
|
|
144
|
+
// Called by the injector with the correct `Promise` for dependencies
|
|
145
|
+
constructor(private _foo: Promise<Foo>) {}
|
|
146
|
+
|
|
147
|
+
// Your code
|
|
148
|
+
bar() {
|
|
149
|
+
await (this._foo).bar()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
119
154
|
Using Factories
|
|
120
155
|
---------------
|
|
121
156
|
|
|
@@ -278,6 +313,11 @@ annotated with _JSDoc_).
|
|
|
278
313
|
Create a new instance of the given injectable (a _class_) injecting all its
|
|
279
314
|
required dependencies
|
|
280
315
|
|
|
316
|
+
* `injections.make(factory: Factory): ReturnType<Factory>` \
|
|
317
|
+
Simple utility method to invoke the factory with the correct `Injections`
|
|
318
|
+
and return its result. This can be used to alleviate issues when top-level
|
|
319
|
+
await is not available.
|
|
320
|
+
|
|
281
321
|
#### Sub Injectors
|
|
282
322
|
|
|
283
323
|
* `injections.injector(): Injector` \
|
package/dist/index.js
CHANGED
|
@@ -2,33 +2,34 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
|
|
6
5
|
var __export = (target, all) => {
|
|
7
6
|
for (var name in all)
|
|
8
7
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
8
|
};
|
|
10
|
-
var
|
|
11
|
-
if (
|
|
12
|
-
for (let key of __getOwnPropNames(
|
|
13
|
-
if (!__hasOwnProp.call(
|
|
14
|
-
__defProp(
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
14
|
}
|
|
16
|
-
return
|
|
15
|
+
return to;
|
|
17
16
|
};
|
|
18
|
-
var __toCommonJS =
|
|
19
|
-
return (module2, temp) => {
|
|
20
|
-
return cache && cache.get(module2) || (temp = __reExport(__markAsModule({}), module2, 1), cache && cache.set(module2, temp), temp);
|
|
21
|
-
};
|
|
22
|
-
})(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0);
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
18
|
|
|
24
19
|
// src/index.ts
|
|
25
20
|
var src_exports = {};
|
|
26
21
|
__export(src_exports, {
|
|
27
|
-
Injector: () => Injector
|
|
22
|
+
Injector: () => Injector,
|
|
23
|
+
promise: () => promise
|
|
28
24
|
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
29
26
|
function bindingName(binding) {
|
|
30
27
|
return typeof binding === "function" ? `[class ${binding.name}]` : `"${binding}"`;
|
|
31
28
|
}
|
|
29
|
+
var promisedBinding = Symbol.for("siringa.promisedBinding");
|
|
30
|
+
function promise(binding) {
|
|
31
|
+
return { [promisedBinding]: binding };
|
|
32
|
+
}
|
|
32
33
|
var Injector = class {
|
|
33
34
|
#factories = /* @__PURE__ */ new Map();
|
|
34
35
|
#promises = /* @__PURE__ */ new Map();
|
|
@@ -40,14 +41,14 @@ var Injector = class {
|
|
|
40
41
|
const message2 = `Recursion detected injecting ${bindingName(binding)}`;
|
|
41
42
|
return Promise.reject(new Error(message2));
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
44
|
+
const promise2 = this.#promises.get(binding);
|
|
45
|
+
if (promise2)
|
|
46
|
+
return promise2;
|
|
46
47
|
const factory = this.#factories.get(binding);
|
|
47
48
|
if (factory) {
|
|
48
|
-
const
|
|
49
|
-
this.#promises.set(binding,
|
|
50
|
-
return
|
|
49
|
+
const promise3 = Promise.resolve().then(() => factory([...stack, binding]));
|
|
50
|
+
this.#promises.set(binding, promise3);
|
|
51
|
+
return promise3;
|
|
51
52
|
}
|
|
52
53
|
if (this.#parent)
|
|
53
54
|
return this.#parent.#get(binding, []);
|
|
@@ -56,8 +57,18 @@ var Injector = class {
|
|
|
56
57
|
}
|
|
57
58
|
async #inject(injectable, stack) {
|
|
58
59
|
var _a;
|
|
59
|
-
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) =>
|
|
60
|
-
|
|
60
|
+
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) => {
|
|
61
|
+
switch (typeof binding) {
|
|
62
|
+
case "string":
|
|
63
|
+
case "function":
|
|
64
|
+
return this.#get(binding, stack);
|
|
65
|
+
default:
|
|
66
|
+
return binding;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const injections = promises ? (await Promise.all(promises)).map((i) => {
|
|
70
|
+
return promisedBinding in i ? this.#get(i[promisedBinding], stack) : i;
|
|
71
|
+
}) : [];
|
|
61
72
|
return new injectable(...injections);
|
|
62
73
|
}
|
|
63
74
|
bind(binding, maybeInjectable) {
|
|
@@ -80,18 +91,25 @@ var Injector = class {
|
|
|
80
91
|
async get(binding) {
|
|
81
92
|
return this.#get(binding, []);
|
|
82
93
|
}
|
|
83
|
-
|
|
94
|
+
inject(injectable) {
|
|
84
95
|
return this.#inject(injectable, []);
|
|
85
96
|
}
|
|
97
|
+
make(factory) {
|
|
98
|
+
return factory({
|
|
99
|
+
get: (component) => this.#get(component, []),
|
|
100
|
+
inject: async (injectable) => this.#inject(injectable, []),
|
|
101
|
+
injector: () => this.injector()
|
|
102
|
+
});
|
|
103
|
+
}
|
|
86
104
|
injector() {
|
|
87
105
|
const injector = new Injector();
|
|
88
106
|
injector.#parent = this;
|
|
89
107
|
return injector;
|
|
90
108
|
}
|
|
91
109
|
};
|
|
92
|
-
module.exports = __toCommonJS(src_exports);
|
|
93
110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
111
|
0 && (module.exports = {
|
|
95
|
-
Injector
|
|
112
|
+
Injector,
|
|
113
|
+
promise
|
|
96
114
|
});
|
|
97
115
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"mappings": "
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
function bindingName(binding) {
|
|
3
3
|
return typeof binding === "function" ? `[class ${binding.name}]` : `"${binding}"`;
|
|
4
4
|
}
|
|
5
|
+
var promisedBinding = Symbol.for("siringa.promisedBinding");
|
|
6
|
+
function promise(binding) {
|
|
7
|
+
return { [promisedBinding]: binding };
|
|
8
|
+
}
|
|
5
9
|
var Injector = class {
|
|
6
10
|
#factories = /* @__PURE__ */ new Map();
|
|
7
11
|
#promises = /* @__PURE__ */ new Map();
|
|
@@ -13,14 +17,14 @@ var Injector = class {
|
|
|
13
17
|
const message2 = `Recursion detected injecting ${bindingName(binding)}`;
|
|
14
18
|
return Promise.reject(new Error(message2));
|
|
15
19
|
}
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
return
|
|
20
|
+
const promise2 = this.#promises.get(binding);
|
|
21
|
+
if (promise2)
|
|
22
|
+
return promise2;
|
|
19
23
|
const factory = this.#factories.get(binding);
|
|
20
24
|
if (factory) {
|
|
21
|
-
const
|
|
22
|
-
this.#promises.set(binding,
|
|
23
|
-
return
|
|
25
|
+
const promise3 = Promise.resolve().then(() => factory([...stack, binding]));
|
|
26
|
+
this.#promises.set(binding, promise3);
|
|
27
|
+
return promise3;
|
|
24
28
|
}
|
|
25
29
|
if (this.#parent)
|
|
26
30
|
return this.#parent.#get(binding, []);
|
|
@@ -29,8 +33,18 @@ var Injector = class {
|
|
|
29
33
|
}
|
|
30
34
|
async #inject(injectable, stack) {
|
|
31
35
|
var _a;
|
|
32
|
-
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) =>
|
|
33
|
-
|
|
36
|
+
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) => {
|
|
37
|
+
switch (typeof binding) {
|
|
38
|
+
case "string":
|
|
39
|
+
case "function":
|
|
40
|
+
return this.#get(binding, stack);
|
|
41
|
+
default:
|
|
42
|
+
return binding;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const injections = promises ? (await Promise.all(promises)).map((i) => {
|
|
46
|
+
return promisedBinding in i ? this.#get(i[promisedBinding], stack) : i;
|
|
47
|
+
}) : [];
|
|
34
48
|
return new injectable(...injections);
|
|
35
49
|
}
|
|
36
50
|
bind(binding, maybeInjectable) {
|
|
@@ -53,9 +67,16 @@ var Injector = class {
|
|
|
53
67
|
async get(binding) {
|
|
54
68
|
return this.#get(binding, []);
|
|
55
69
|
}
|
|
56
|
-
|
|
70
|
+
inject(injectable) {
|
|
57
71
|
return this.#inject(injectable, []);
|
|
58
72
|
}
|
|
73
|
+
make(factory) {
|
|
74
|
+
return factory({
|
|
75
|
+
get: (component) => this.#get(component, []),
|
|
76
|
+
inject: async (injectable) => this.#inject(injectable, []),
|
|
77
|
+
injector: () => this.injector()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
59
80
|
injector() {
|
|
60
81
|
const injector = new Injector();
|
|
61
82
|
injector.#parent = this;
|
|
@@ -63,6 +84,7 @@ var Injector = class {
|
|
|
63
84
|
}
|
|
64
85
|
};
|
|
65
86
|
export {
|
|
66
|
-
Injector
|
|
87
|
+
Injector,
|
|
88
|
+
promise
|
|
67
89
|
};
|
|
68
90
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"mappings": ";
|
|
4
|
+
"mappings": ";AA2JA,qBAAqB,SAA0B;AAC7C,SAAO,OAAO,YAAY,aAAa,UAAU,QAAQ,UAAU,IAAI;AACzE;AAGA,IAAM,kBAAkB,OAAO,IAAI,yBAAyB;AAGrD,iBAAoC,SAAgC;AACzE,SAAO,GAAG,kBAAkB,QAAQ;AACtC;AA2DO,qBAGyC;AAAA,EACrC,aAA+D,oBAAI,IAAI;AAAA,EACvE,YAAwC,oBAAI,IAAI;AAAA,EACzD;AAAA,QAIM,KAAK,SAAkB,OAAgC;AAC3D,QAAI,MAAM,SAAS,OAAO,GAAG;AAC3B,UAAI;AAAc,eAAO,kBAAkB,SAAS,CAAC,CAAC;AACtD,YAAM,WAAU,gCAAgC,YAAY,OAAO;AACnE,aAAO,QAAQ,OAAO,IAAI,MAAM,QAAO,CAAC;AAAA,IAC1C;AAEA,UAAM,WAAU,eAAe,IAAI,OAAO;AAC1C,QAAI;AAAS,aAAO;AAEpB,UAAM,UAAU,gBAAgB,IAAI,OAAO;AAC3C,QAAI,SAAS;AACX,YAAM,WAAU,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,CAAE,GAAG,OAAO,OAAQ,CAAC,CAAC;AAC3E,qBAAe,IAAI,SAAS,QAAO;AACnC,aAAO;AAAA,IACT;AAEA,QAAI;AAAc,aAAO,kBAAkB,SAAS,CAAC,CAAC;AAEtD,UAAM,UAAU,6BAA6B,YAAY,OAAO;AAChE,WAAO,QAAQ,OAAO,IAAI,MAAM,OAAO,CAAC;AAAA,EAC1C;AAAA,QAEM,QAAQ,YAAgD,OAAgC;AAjQhG;AAkQI,UAAM,WAAW,iBAAW,YAAX,mBAAoB,IAAI,CAAC,YAAuC;AAC/E,cAAQ,OAAO;AAAA,aACR;AAAA,aACA;AACH,iBAAO,UAAU,SAAS,KAAK;AAAA;AAE/B,iBAAO;AAAA;AAAA,IAEb;AAEA,UAAM,aAAa,WAAY,OAAM,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM;AACrE,aAAO,mBAAmB,IAAI,UAAU,EAAE,kBAAkB,KAAK,IAAI;AAAA,IACvE,CAAC,IAAI,CAAC;AAGN,WAAO,IAAI,WAAW,GAAG,UAAU;AAAA,EACrC;AAAA,EAsBA,KACE,SACA,iBACM;AACN,UAAM,aAAa,kBAAkB,kBACjC;AAEJ,oBAAgB,IAAI,SAAS,OAAO,UAAU,aAAa,YAAY,KAAK,CAAC;AAC7E,WAAO;AAAA,EACT;AAAA,EAiBA,OACE,SACA,SACM;AACN,oBAAgB,IAAI,SAAS,OAAO,UAAU,QAAQ;AAAA,MACpD,KAAK,CAAC,cAAmB,UAAU,WAAW,KAAK;AAAA,MACnD,QAAQ,OAAO,eAAoB,aAAa,YAAY,KAAK;AAAA,MACjE,UAAU,MAAM,KAAK,SAAS;AAAA,IAChC,CAAC,CAAC;AAEF,WAAO;AAAA,EACT;AAAA,EAiBA,IACE,SACA,UACM;AACN,mBAAe,IAAI,SAAS,QAAQ,QAAQ,QAAQ,CAAC;AACrD,WAAO;AAAA,EACT;AAAA,QAWM,IACJ,SACc;AACd,WAAO,UAAU,SAAS,CAAC,CAAC;AAAA,EAC9B;AAAA,EAUA,OACE,YAC0B;AAC1B,WAAO,aAAa,YAAY,CAAC,CAAC;AAAA,EACpC;AAAA,EAQA,KACE,SACe;AACf,WAAO,QAAQ;AAAA,MACb,KAAK,CAAC,cAAmB,UAAU,WAAW,CAAC,CAAC;AAAA,MAChD,QAAQ,OAAO,eAAoB,aAAa,YAAY,CAAC,CAAC;AAAA,MAC9D,UAAU,MAAM,KAAK,SAAS;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAKA,WAA6C;AAC3C,UAAM,WAAW,IAAI,SAAiC;AACtD,uBAAmB;AACnB,WAAO;AAAA,EACT;AACF;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
/** A generic constructor */
|
|
2
2
|
declare type Constructor<T = any> = abstract new (...args: any) => T;
|
|
3
|
+
/** The _type_ for a binding, either a `Constructor` or a `string`. */
|
|
4
|
+
declare type Binding = Constructor | string;
|
|
5
|
+
/** The _type_ of a binding in the context of an `Injector`. */
|
|
6
|
+
declare type InjectorBinding<Components extends Constructor, Provisions extends Record<string, any>> = Components | (keyof Provisions & string) | PromisedBinding<Components | (keyof Provisions & string)>;
|
|
7
|
+
/** A tuple of `InjectorBinding`s (what's needed by `$inject`). */
|
|
8
|
+
declare type InjectTuple<Components extends Constructor, Provisions extends Record<string, any>> = readonly [InjectorBinding<Components, Provisions>, ...(InjectorBinding<Components, Provisions>)[]];
|
|
3
9
|
/**
|
|
4
10
|
* Check that an `Injector`'s static `$inject` member matches the `Injector`'s
|
|
5
11
|
* own components and provisions
|
|
6
12
|
*/
|
|
7
13
|
declare type CheckInject<Inject, // the tuple from "$inject"
|
|
8
14
|
Components extends Constructor, // the `Injector`'s components
|
|
9
|
-
Provisions extends Record<string, any>> = Inject extends readonly [] ? readonly [] : Inject extends readonly [infer I1] ? I1 extends keyof Provisions ? readonly [I1] : I1 extends Extract<Components, I1> ? readonly [I1] : readonly [never] : Inject extends readonly [infer I1, ...infer I2] ? readonly [
|
|
15
|
+
Provisions extends Record<string, any>> = Inject extends readonly [] ? readonly [] : Inject extends readonly [infer I1] ? I1 extends PromisedBinding<infer I2> ? I2 extends keyof Provisions ? readonly [PromisedBinding<I2>] : I2 extends Extract<Components, I2> ? readonly [PromisedBinding<I2>] : readonly [PromisedBinding<never>] : I1 extends keyof Provisions ? readonly [I1] : I1 extends Extract<Components, I1> ? readonly [I1] : readonly [never] : Inject extends readonly [infer I1, ...infer I2] ? readonly [
|
|
10
16
|
...CheckInject<[I1], Components, Provisions>,
|
|
11
17
|
...CheckInject<I2, Components, Provisions>
|
|
12
18
|
] : readonly [Components | keyof Provisions, ...(Components | keyof Provisions)[]];
|
|
@@ -15,7 +21,7 @@ Provisions extends Record<string, any>> = Inject extends readonly [] ? readonly
|
|
|
15
21
|
* named provisions.
|
|
16
22
|
*/
|
|
17
23
|
declare type MapInject<Inject, // the tuple from "$inject"
|
|
18
|
-
Provisions extends Record<string, any>> = Inject extends readonly [] ? readonly [] : Inject extends readonly [infer I1] ? I1 extends keyof Provisions ? readonly [Provisions[I1]] : I1 extends Constructor ? readonly [InstanceType<I1>] : readonly [never] : Inject extends readonly [infer I1, ...infer I2] ? readonly [
|
|
24
|
+
Provisions extends Record<string, any>> = Inject extends readonly [] ? readonly [] : Inject extends readonly [infer I1] ? I1 extends PromisedBinding<infer I2> ? I2 extends keyof Provisions ? readonly [Promise<Provisions[I2]>] : I2 extends Constructor ? readonly [Promise<InstanceType<I2>>] : readonly [never] : I1 extends keyof Provisions ? readonly [Provisions[I1]] : I1 extends Constructor ? readonly [InstanceType<I1>] : readonly [never] : Inject extends readonly [infer I1, ...infer I2] ? readonly [
|
|
19
25
|
...MapInject<[I1], Provisions>,
|
|
20
26
|
...MapInject<I2, Provisions>
|
|
21
27
|
] : readonly [never];
|
|
@@ -30,7 +36,7 @@ Provisions extends Record<string, any>> = I extends {
|
|
|
30
36
|
$inject: CheckInject<Inject, Components, Provisions>;
|
|
31
37
|
new (...args: MapInject<Inject, Provisions>): any;
|
|
32
38
|
} : I extends new () => any ? I : {
|
|
33
|
-
$inject:
|
|
39
|
+
$inject: InjectTuple<Components, Provisions>;
|
|
34
40
|
};
|
|
35
41
|
/**
|
|
36
42
|
* Return the unrolled type of a `Promise`
|
|
@@ -44,18 +50,25 @@ P extends string, // the new (string) key of the provision
|
|
|
44
50
|
T> = {
|
|
45
51
|
[key in P | keyof Provisions]: key extends P ? UnrollPromise<T> : Provisions[key];
|
|
46
52
|
};
|
|
53
|
+
/** A constant symbol identifying a _promised binding_. */
|
|
54
|
+
declare const promisedBinding: unique symbol;
|
|
55
|
+
/** Declare a _binding_ to be _promised_ (inject its `Promise`). */
|
|
56
|
+
export declare function promise<B extends Binding>(binding: B): PromisedBinding<B>;
|
|
57
|
+
export interface PromisedBinding<B extends Binding = Binding> {
|
|
58
|
+
[promisedBinding]: B;
|
|
59
|
+
}
|
|
47
60
|
/** An `Injectable` defines a constructor for an injectable class */
|
|
48
61
|
export interface Injectable<Components extends Constructor, Provisions extends Record<string, any>, T = any> {
|
|
49
62
|
prototype: T;
|
|
50
63
|
new (...args: any): T;
|
|
51
|
-
$inject?:
|
|
64
|
+
$inject?: InjectTuple<Components, Provisions>;
|
|
52
65
|
}
|
|
53
66
|
/**
|
|
54
67
|
* The `Injections` interface abstracts the idea of getting bound and
|
|
55
68
|
* provisioned instances from an `Injector`, injecting new `Injectable`
|
|
56
69
|
* instances, and creating sub-`Injector`s.
|
|
57
70
|
*/
|
|
58
|
-
export interface Injections<Components extends Constructor, Provisions extends Record<string, any
|
|
71
|
+
export interface Injections<Components extends Constructor = never, Provisions extends Record<string, any> = {}> {
|
|
59
72
|
/** Get a _bound_ instance from an `Injector`. */
|
|
60
73
|
get<C extends Components>(component: C): Promise<InstanceType<C>>;
|
|
61
74
|
/** Get a _provisioned_ instance from an `Injector`. */
|
|
@@ -89,9 +102,9 @@ export declare class Injector<Components extends Constructor = never, Provisions
|
|
|
89
102
|
/** Use a `Factory` to create instances bound to the given name. */
|
|
90
103
|
create<P extends string, F extends Factory<Components, Provisions>>(provision: P, factory: F): Injector<Components, ExtendProvisions<Provisions, P, ReturnType<F>>>;
|
|
91
104
|
/** Use the given instance binding it to to the given `Constructor`. */
|
|
92
|
-
use<C extends Constructor>(component: C, instance: InstanceType<C>): Injector<Components | C, Provisions>;
|
|
105
|
+
use<C extends Constructor>(component: C, instance: InstanceType<C> | PromiseLike<InstanceType<C>>): Injector<Components | C, Provisions>;
|
|
93
106
|
/** Use the given instance binding it to to the given name. */
|
|
94
|
-
use<P extends string, T>(provision: P, instance: T): Injector<Components, ExtendProvisions<Provisions, P, T>>;
|
|
107
|
+
use<P extends string, T>(provision: P, instance: T | PromiseLike<T>): Injector<Components, ExtendProvisions<Provisions, P, T>>;
|
|
95
108
|
/** Get a _bound_ instance from an `Injector` */
|
|
96
109
|
get<C extends Components>(component: C): Promise<InstanceType<C>>;
|
|
97
110
|
/** Get a _provisioned_ instance from an `Injector` */
|
|
@@ -103,6 +116,13 @@ export declare class Injector<Components extends Constructor = never, Provisions
|
|
|
103
116
|
* @param injectable The constructor of the instance to create.
|
|
104
117
|
*/
|
|
105
118
|
inject<I extends Injectable<Components, Provisions>>(injectable: I & CheckInjectable<I, Components, Provisions>): Promise<InstanceType<I>>;
|
|
119
|
+
/**
|
|
120
|
+
* Simple utility method to invoke the factory with the correct `Injections`
|
|
121
|
+
* and return its result.
|
|
122
|
+
*
|
|
123
|
+
* This can be used to alleviate issues when top-level await is not available.
|
|
124
|
+
*/
|
|
125
|
+
make<F extends Factory<Components, Provisions>>(factory: F): ReturnType<F>;
|
|
106
126
|
/** Create a sub-`Injector` child of this one. */
|
|
107
127
|
injector(): Injector<Components, Provisions>;
|
|
108
128
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "siringa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Simple type-checking injection library for Typescript",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
"@types/chai-as-promised": "^7.1.5",
|
|
25
25
|
"@types/mocha": "^9.1.0",
|
|
26
26
|
"@types/node": "~16.11.26",
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
28
|
-
"@typescript-eslint/parser": "^5.
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
|
28
|
+
"@typescript-eslint/parser": "^5.17.0",
|
|
29
29
|
"chai": "^4.3.6",
|
|
30
30
|
"chai-as-promised": "^7.1.1",
|
|
31
|
-
"esbuild": "^0.14.
|
|
32
|
-
"eslint": "^8.
|
|
31
|
+
"esbuild": "^0.14.28",
|
|
32
|
+
"eslint": "^8.12.0",
|
|
33
33
|
"eslint-config-google": "^0.14.0",
|
|
34
|
-
"mocha": "^9.2.
|
|
34
|
+
"mocha": "^9.2.2",
|
|
35
35
|
"nodemon": "^2.0.15",
|
|
36
36
|
"nyc": "^15.1.0",
|
|
37
37
|
"source-map-support": "^0.5.21",
|
|
38
38
|
"tsd": "^0.19.1",
|
|
39
|
-
"typescript": "^4.
|
|
39
|
+
"typescript": "^4.6.3"
|
|
40
40
|
},
|
|
41
41
|
"directories": {
|
|
42
42
|
"test": "test"
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
/** A generic constructor */
|
|
2
2
|
type Constructor<T = any> = abstract new (...args: any) => T
|
|
3
3
|
|
|
4
|
+
/** The _type_ for a binding, either a `Constructor` or a `string`. */
|
|
5
|
+
type Binding = Constructor | string
|
|
6
|
+
|
|
7
|
+
/** The _type_ of a binding in the context of an `Injector`. */
|
|
8
|
+
type InjectorBinding<
|
|
9
|
+
Components extends Constructor,
|
|
10
|
+
Provisions extends Record<string, any>,
|
|
11
|
+
> = Components | (keyof Provisions & string) | PromisedBinding<Components | (keyof Provisions & string)>
|
|
12
|
+
|
|
13
|
+
/** A tuple of `InjectorBinding`s (what's needed by `$inject`). */
|
|
14
|
+
type InjectTuple<
|
|
15
|
+
Components extends Constructor,
|
|
16
|
+
Provisions extends Record<string, any>,
|
|
17
|
+
> = readonly [ InjectorBinding<Components, Provisions>, ...(InjectorBinding<Components, Provisions>)[] ]
|
|
18
|
+
|
|
19
|
+
|
|
4
20
|
/* ========================================================================== */
|
|
5
21
|
|
|
6
22
|
/**
|
|
@@ -19,6 +35,12 @@ type CheckInject<
|
|
|
19
35
|
// if "$inject" is a single-element tuple, check that that this element is
|
|
20
36
|
// actually included in the list of components
|
|
21
37
|
Inject extends readonly [ infer I1 ] ?
|
|
38
|
+
I1 extends PromisedBinding<infer I2> ?
|
|
39
|
+
I2 extends keyof Provisions ?
|
|
40
|
+
readonly [ PromisedBinding<I2> ] :
|
|
41
|
+
I2 extends Extract<Components, I2> ?
|
|
42
|
+
readonly [ PromisedBinding<I2> ] :
|
|
43
|
+
readonly [ PromisedBinding<never> ] :
|
|
22
44
|
I1 extends keyof Provisions ?
|
|
23
45
|
readonly [ I1 ] :
|
|
24
46
|
I1 extends Extract<Components, I1> ?
|
|
@@ -53,6 +75,14 @@ type MapInject<
|
|
|
53
75
|
|
|
54
76
|
// "$inject" is a single-element tuple
|
|
55
77
|
Inject extends readonly [ infer I1 ] ?
|
|
78
|
+
I1 extends PromisedBinding<infer I2> ?
|
|
79
|
+
// promised bindings
|
|
80
|
+
I2 extends keyof Provisions ?
|
|
81
|
+
readonly [ Promise<Provisions[I2]> ] :
|
|
82
|
+
I2 extends Constructor ?
|
|
83
|
+
readonly [ Promise<InstanceType<I2>> ] :
|
|
84
|
+
readonly [ never ] :
|
|
85
|
+
// resolved (non promised) bindings
|
|
56
86
|
I1 extends keyof Provisions ?
|
|
57
87
|
readonly [ Provisions[I1] ] :
|
|
58
88
|
I1 extends Constructor ?
|
|
@@ -93,7 +123,7 @@ type CheckInjectable<
|
|
|
93
123
|
I :
|
|
94
124
|
|
|
95
125
|
// Anything else requires "$inject" to be present
|
|
96
|
-
{ $inject:
|
|
126
|
+
{ $inject: InjectTuple<Components, Provisions> }
|
|
97
127
|
|
|
98
128
|
/* ========================================================================== */
|
|
99
129
|
|
|
@@ -118,17 +148,28 @@ type ExtendProvisions<
|
|
|
118
148
|
Provisions[key]
|
|
119
149
|
}
|
|
120
150
|
|
|
121
|
-
/* ==========================================================================
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
/* ========================================================================== *
|
|
152
|
+
* EXPORTED TYPES *
|
|
153
|
+
* ========================================================================== */
|
|
124
154
|
|
|
155
|
+
/** Utility to nicely print a binding name */
|
|
125
156
|
function bindingName(binding: Binding): string {
|
|
126
157
|
return typeof binding === 'function' ? `[class ${binding.name}]` : `"${binding}"`
|
|
127
158
|
}
|
|
128
159
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
/** A constant symbol identifying a _promised binding_. */
|
|
161
|
+
const promisedBinding = Symbol.for('siringa.promisedBinding')
|
|
162
|
+
|
|
163
|
+
/** Declare a _binding_ to be _promised_ (inject its `Promise`). */
|
|
164
|
+
export function promise<B extends Binding>(binding: B): PromisedBinding<B> {
|
|
165
|
+
return { [promisedBinding]: binding }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ========================================================================== */
|
|
169
|
+
|
|
170
|
+
export interface PromisedBinding<B extends Binding = Binding> {
|
|
171
|
+
[promisedBinding]: B
|
|
172
|
+
}
|
|
132
173
|
|
|
133
174
|
/** An `Injectable` defines a constructor for an injectable class */
|
|
134
175
|
export interface Injectable<
|
|
@@ -138,7 +179,7 @@ export interface Injectable<
|
|
|
138
179
|
> {
|
|
139
180
|
prototype: T
|
|
140
181
|
new (...args: any): T
|
|
141
|
-
$inject?:
|
|
182
|
+
$inject?: InjectTuple<Components, Provisions>
|
|
142
183
|
}
|
|
143
184
|
|
|
144
185
|
/**
|
|
@@ -147,8 +188,8 @@ export interface Injectable<
|
|
|
147
188
|
* instances, and creating sub-`Injector`s.
|
|
148
189
|
*/
|
|
149
190
|
export interface Injections<
|
|
150
|
-
Components extends Constructor,
|
|
151
|
-
Provisions extends Record<string, any
|
|
191
|
+
Components extends Constructor = never,
|
|
192
|
+
Provisions extends Record<string, any> = {},
|
|
152
193
|
> {
|
|
153
194
|
/** Get a _bound_ instance from an `Injector`. */
|
|
154
195
|
get<C extends Components>(component: C): Promise<InstanceType<C>>
|
|
@@ -215,8 +256,19 @@ export class Injector<
|
|
|
215
256
|
}
|
|
216
257
|
|
|
217
258
|
async #inject(injectable: Injectable<Components, Provisions>, stack: Binding[]): Promise<any> {
|
|
218
|
-
const promises = injectable.$inject?.map((binding:
|
|
219
|
-
|
|
259
|
+
const promises = injectable.$inject?.map((binding: Binding | PromisedBinding) => {
|
|
260
|
+
switch (typeof binding) {
|
|
261
|
+
case 'string':
|
|
262
|
+
case 'function':
|
|
263
|
+
return this.#get(binding, stack)
|
|
264
|
+
default:
|
|
265
|
+
return binding
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
const injections = promises ? (await Promise.all(promises)).map((i) => {
|
|
270
|
+
return promisedBinding in i ? this.#get(i[promisedBinding], stack) : i
|
|
271
|
+
}) : []
|
|
220
272
|
|
|
221
273
|
// eslint-disable-next-line new-cap
|
|
222
274
|
return new injectable(...injections)
|
|
@@ -286,13 +338,13 @@ export class Injector<
|
|
|
286
338
|
/** Use the given instance binding it to to the given `Constructor`. */
|
|
287
339
|
use<C extends Constructor>(
|
|
288
340
|
component: C,
|
|
289
|
-
instance: InstanceType<C
|
|
341
|
+
instance: InstanceType<C> | PromiseLike<InstanceType<C>>,
|
|
290
342
|
): Injector<Components | C, Provisions>
|
|
291
343
|
|
|
292
344
|
/** Use the given instance binding it to to the given name. */
|
|
293
345
|
use<P extends string, T>(
|
|
294
346
|
provision: P,
|
|
295
|
-
instance: T
|
|
347
|
+
instance: T | PromiseLike<T>,
|
|
296
348
|
): Injector<Components, ExtendProvisions<Provisions, P, T>>
|
|
297
349
|
|
|
298
350
|
// Overloaded implementation
|
|
@@ -319,18 +371,36 @@ export class Injector<
|
|
|
319
371
|
return this.#get(binding, [])
|
|
320
372
|
}
|
|
321
373
|
|
|
374
|
+
/* INJECTIONS ============================================================= */
|
|
375
|
+
|
|
322
376
|
/**
|
|
323
377
|
* Create a new instance of the specified `Injectable`, providing all
|
|
324
378
|
* necessary injections.
|
|
325
379
|
*
|
|
326
380
|
* @param injectable The constructor of the instance to create.
|
|
327
381
|
*/
|
|
328
|
-
|
|
382
|
+
inject<I extends Injectable<Components, Provisions>>(
|
|
329
383
|
injectable: I & CheckInjectable<I, Components, Provisions>,
|
|
330
384
|
): Promise<InstanceType<I>> {
|
|
331
385
|
return this.#inject(injectable, [])
|
|
332
386
|
}
|
|
333
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Simple utility method to invoke the factory with the correct `Injections`
|
|
390
|
+
* and return its result.
|
|
391
|
+
*
|
|
392
|
+
* This can be used to alleviate issues when top-level await is not available.
|
|
393
|
+
*/
|
|
394
|
+
make<F extends Factory<Components, Provisions>>(
|
|
395
|
+
factory: F,
|
|
396
|
+
): ReturnType<F> {
|
|
397
|
+
return factory({
|
|
398
|
+
get: (component: any) => this.#get(component, []),
|
|
399
|
+
inject: async (injectable: any) => this.#inject(injectable, []),
|
|
400
|
+
injector: () => this.injector(),
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
334
404
|
/* CHILD INJECTORS ======================================================== */
|
|
335
405
|
|
|
336
406
|
/** Create a sub-`Injector` child of this one. */
|