siringa 0.0.2 → 0.0.3
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 +35 -0
- package/dist/index.js +26 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -9
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +19 -6
- package/package.json +5 -5
- package/src/index.ts +64 -12
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
|
|
package/dist/index.js
CHANGED
|
@@ -24,11 +24,16 @@ var __toCommonJS = /* @__PURE__ */ ((cache) => {
|
|
|
24
24
|
// src/index.ts
|
|
25
25
|
var src_exports = {};
|
|
26
26
|
__export(src_exports, {
|
|
27
|
-
Injector: () => Injector
|
|
27
|
+
Injector: () => Injector,
|
|
28
|
+
promise: () => promise
|
|
28
29
|
});
|
|
29
30
|
function bindingName(binding) {
|
|
30
31
|
return typeof binding === "function" ? `[class ${binding.name}]` : `"${binding}"`;
|
|
31
32
|
}
|
|
33
|
+
var promisedBinding = Symbol.for("siringa.promisedBinding");
|
|
34
|
+
function promise(binding) {
|
|
35
|
+
return { [promisedBinding]: binding };
|
|
36
|
+
}
|
|
32
37
|
var Injector = class {
|
|
33
38
|
#factories = /* @__PURE__ */ new Map();
|
|
34
39
|
#promises = /* @__PURE__ */ new Map();
|
|
@@ -40,14 +45,14 @@ var Injector = class {
|
|
|
40
45
|
const message2 = `Recursion detected injecting ${bindingName(binding)}`;
|
|
41
46
|
return Promise.reject(new Error(message2));
|
|
42
47
|
}
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
48
|
+
const promise2 = this.#promises.get(binding);
|
|
49
|
+
if (promise2)
|
|
50
|
+
return promise2;
|
|
46
51
|
const factory = this.#factories.get(binding);
|
|
47
52
|
if (factory) {
|
|
48
|
-
const
|
|
49
|
-
this.#promises.set(binding,
|
|
50
|
-
return
|
|
53
|
+
const promise3 = Promise.resolve().then(() => factory([...stack, binding]));
|
|
54
|
+
this.#promises.set(binding, promise3);
|
|
55
|
+
return promise3;
|
|
51
56
|
}
|
|
52
57
|
if (this.#parent)
|
|
53
58
|
return this.#parent.#get(binding, []);
|
|
@@ -56,8 +61,18 @@ var Injector = class {
|
|
|
56
61
|
}
|
|
57
62
|
async #inject(injectable, stack) {
|
|
58
63
|
var _a;
|
|
59
|
-
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) =>
|
|
60
|
-
|
|
64
|
+
const promises = (_a = injectable.$inject) == null ? void 0 : _a.map((binding) => {
|
|
65
|
+
switch (typeof binding) {
|
|
66
|
+
case "string":
|
|
67
|
+
case "function":
|
|
68
|
+
return this.#get(binding, stack);
|
|
69
|
+
default:
|
|
70
|
+
return binding;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const injections = promises ? (await Promise.all(promises)).map((i) => {
|
|
74
|
+
return promisedBinding in i ? this.#get(i[promisedBinding], stack) : i;
|
|
75
|
+
}) : [];
|
|
61
76
|
return new injectable(...injections);
|
|
62
77
|
}
|
|
63
78
|
bind(binding, maybeInjectable) {
|
|
@@ -92,6 +107,7 @@ var Injector = class {
|
|
|
92
107
|
module.exports = __toCommonJS(src_exports);
|
|
93
108
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
109
|
0 && (module.exports = {
|
|
95
|
-
Injector
|
|
110
|
+
Injector,
|
|
111
|
+
promise
|
|
96
112
|
});
|
|
97
113
|
//# 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": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2JA,qBAAqB,SAA0B;AAC7C,SAAO,OAAO,YAAY,aAAa,UAAU,QAAQ,UAAU,IAAI;AAAA;AAIzE,IAAM,kBAAkB,OAAO,IAAI;AAG5B,iBAAoC,SAAgC;AACzE,SAAO,GAAG,kBAAkB;AAAA;AA4DvB,qBAGyC;AAAA,EACrC,aAA+D,oBAAI;AAAA,EACnE,YAAwC,oBAAI;AAAA,EACrD;AAAA,QAIM,KAAK,SAAkB,OAAgC;AAC3D,QAAI,MAAM,SAAS,UAAU;AAC3B,UAAI;AAAc,eAAO,kBAAkB,SAAS;AACpD,YAAM,WAAU,gCAAgC,YAAY;AAC5D,aAAO,QAAQ,OAAO,IAAI,MAAM;AAAA;AAGlC,UAAM,WAAU,eAAe,IAAI;AACnC,QAAI;AAAS,aAAO;AAEpB,UAAM,UAAU,gBAAgB,IAAI;AACpC,QAAI,SAAS;AACX,YAAM,WAAU,QAAQ,UAAU,KAAK,MAAM,QAAQ,CAAE,GAAG,OAAO;AACjE,qBAAe,IAAI,SAAS;AAC5B,aAAO;AAAA;AAGT,QAAI;AAAc,aAAO,kBAAkB,SAAS;AAEpD,UAAM,UAAU,6BAA6B,YAAY;AACzD,WAAO,QAAQ,OAAO,IAAI,MAAM;AAAA;AAAA,QAG5B,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;AAAA;AAE1B,iBAAO;AAAA;AAAA;AAIb,UAAM,aAAa,WAAY,OAAM,QAAQ,IAAI,WAAW,IAAI,CAAC,MAAM;AACrE,aAAO,mBAAmB,IAAI,UAAU,EAAE,kBAAkB,SAAS;AAAA,SAClE;AAGL,WAAO,IAAI,WAAW,GAAG;AAAA;AAAA,EAuB3B,KACE,SACA,iBACM;AACN,UAAM,aAAa,kBAAkB,kBACjC;AAEJ,oBAAgB,IAAI,SAAS,OAAO,UAAU,aAAa,YAAY;AACvE,WAAO;AAAA;AAAA,EAkBT,OACE,SACA,SACM;AACN,oBAAgB,IAAI,SAAS,OAAO,UAAU,QAAQ;AAAA,MACpD,KAAK,CAAC,cAAmB,UAAU,WAAW;AAAA,MAC9C,QAAQ,OAAO,eAAoB,aAAa,YAAY;AAAA,MAC5D,UAAU,MAAM,KAAK;AAAA;AAGvB,WAAO;AAAA;AAAA,EAkBT,IACE,SACA,UACM;AACN,mBAAe,IAAI,SAAS,QAAQ,QAAQ;AAC5C,WAAO;AAAA;AAAA,QAYH,IACJ,SACc;AACd,WAAO,UAAU,SAAS;AAAA;AAAA,QAStB,OACJ,YAC0B;AAC1B,WAAO,aAAa,YAAY;AAAA;AAAA,EAMlC,WAA6C;AAC3C,UAAM,WAAW,IAAI;AACrB,uBAAmB;AACnB,WAAO;AAAA;AAAA;",
|
|
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) {
|
|
@@ -63,6 +77,7 @@ var Injector = class {
|
|
|
63
77
|
}
|
|
64
78
|
};
|
|
65
79
|
export {
|
|
66
|
-
Injector
|
|
80
|
+
Injector,
|
|
81
|
+
promise
|
|
67
82
|
};
|
|
68
83
|
//# 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;AAAA;AAIzE,IAAM,kBAAkB,OAAO,IAAI;AAG5B,iBAAoC,SAAgC;AACzE,SAAO,GAAG,kBAAkB;AAAA;AA4DvB,qBAGyC;AAAA,EACrC,aAA+D,oBAAI;AAAA,EACnE,YAAwC,oBAAI;AAAA,EACrD;AAAA,QAIM,KAAK,SAAkB,OAAgC;AAC3D,QAAI,MAAM,SAAS,UAAU;AAC3B,UAAI;AAAc,eAAO,kBAAkB,SAAS;AACpD,YAAM,WAAU,gCAAgC,YAAY;AAC5D,aAAO,QAAQ,OAAO,IAAI,MAAM;AAAA;AAGlC,UAAM,WAAU,eAAe,IAAI;AACnC,QAAI;AAAS,aAAO;AAEpB,UAAM,UAAU,gBAAgB,IAAI;AACpC,QAAI,SAAS;AACX,YAAM,WAAU,QAAQ,UAAU,KAAK,MAAM,QAAQ,CAAE,GAAG,OAAO;AACjE,qBAAe,IAAI,SAAS;AAC5B,aAAO;AAAA;AAGT,QAAI;AAAc,aAAO,kBAAkB,SAAS;AAEpD,UAAM,UAAU,6BAA6B,YAAY;AACzD,WAAO,QAAQ,OAAO,IAAI,MAAM;AAAA;AAAA,QAG5B,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;AAAA;AAE1B,iBAAO;AAAA;AAAA;AAIb,UAAM,aAAa,WAAY,OAAM,QAAQ,IAAI,WAAW,IAAI,CAAC,MAAM;AACrE,aAAO,mBAAmB,IAAI,UAAU,EAAE,kBAAkB,SAAS;AAAA,SAClE;AAGL,WAAO,IAAI,WAAW,GAAG;AAAA;AAAA,EAuB3B,KACE,SACA,iBACM;AACN,UAAM,aAAa,kBAAkB,kBACjC;AAEJ,oBAAgB,IAAI,SAAS,OAAO,UAAU,aAAa,YAAY;AACvE,WAAO;AAAA;AAAA,EAkBT,OACE,SACA,SACM;AACN,oBAAgB,IAAI,SAAS,OAAO,UAAU,QAAQ;AAAA,MACpD,KAAK,CAAC,cAAmB,UAAU,WAAW;AAAA,MAC9C,QAAQ,OAAO,eAAoB,aAAa,YAAY;AAAA,MAC5D,UAAU,MAAM,KAAK;AAAA;AAGvB,WAAO;AAAA;AAAA,EAkBT,IACE,SACA,UACM;AACN,mBAAe,IAAI,SAAS,QAAQ,QAAQ;AAC5C,WAAO;AAAA;AAAA,QAYH,IACJ,SACc;AACd,WAAO,UAAU,SAAS;AAAA;AAAA,QAStB,OACJ,YAC0B;AAC1B,WAAO,aAAa,YAAY;AAAA;AAAA,EAMlC,WAA6C;AAC3C,UAAM,WAAW,IAAI;AACrB,uBAAmB;AACnB,WAAO;AAAA;AAAA;",
|
|
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,11 +50,18 @@ 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
|
|
@@ -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` */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "siringa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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.13.0",
|
|
28
|
+
"@typescript-eslint/parser": "^5.13.0",
|
|
29
29
|
"chai": "^4.3.6",
|
|
30
30
|
"chai-as-promised": "^7.1.1",
|
|
31
31
|
"esbuild": "^0.14.23",
|
|
32
|
-
"eslint": "^8.
|
|
32
|
+
"eslint": "^8.10.0",
|
|
33
33
|
"eslint-config-google": "^0.14.0",
|
|
34
34
|
"mocha": "^9.2.1",
|
|
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.2"
|
|
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
|
/**
|
|
@@ -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
|