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 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 promise = this.#promises.get(binding);
44
- if (promise)
45
- return promise;
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 promise2 = Promise.resolve().then(() => factory([...stack, binding]));
49
- this.#promises.set(binding, promise2);
50
- return promise2;
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) => this.#get(binding, stack));
60
- const injections = promises ? await Promise.all(promises) : [];
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;AA4HA,qBAAqB,SAA0B;AAC7C,SAAO,OAAO,YAAY,aAAa,UAAU,QAAQ,UAAU,IAAI;AAAA;AA0DlE,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,UAAU,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;AAxNhG;AAyNI,UAAM,WAAW,iBAAW,YAAX,mBAAoB,IAAI,CAAC,YAAiB,UAAU,SAAS;AAC9E,UAAM,aAAa,WAAW,MAAM,QAAQ,IAAI,YAAY;AAG5D,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;",
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 promise = this.#promises.get(binding);
17
- if (promise)
18
- return promise;
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 promise2 = Promise.resolve().then(() => factory([...stack, binding]));
22
- this.#promises.set(binding, promise2);
23
- return promise2;
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) => this.#get(binding, stack));
33
- const injections = promises ? await Promise.all(promises) : [];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "mappings": ";AA4HA,qBAAqB,SAA0B;AAC7C,SAAO,OAAO,YAAY,aAAa,UAAU,QAAQ,UAAU,IAAI;AAAA;AA0DlE,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,UAAU,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;AAxNhG;AAyNI,UAAM,WAAW,iBAAW,YAAX,mBAAoB,IAAI,CAAC,YAAiB,UAAU,SAAS;AAC9E,UAAM,aAAa,WAAW,MAAM,QAAQ,IAAI,YAAY;AAG5D,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;",
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: readonly [Components | keyof Provisions, ...(Components | keyof Provisions)[]];
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?: readonly (Components | keyof Provisions)[];
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.2",
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.12.1",
28
- "@typescript-eslint/parser": "^5.12.1",
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.9.0",
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.5.5"
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: readonly [ Components | keyof Provisions, ...(Components | keyof Provisions)[] ] }
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
- type Binding = Constructor | string
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
- * EXPORTED TYPES *
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?: readonly (Components | keyof Provisions)[]
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: any) => this.#get(binding, stack))
219
- const injections = promises ? await Promise.all(promises) : []
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