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 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 __reExport = (target, module2, copyDefault, desc) => {
11
- if (module2 && typeof module2 === "object" || typeof module2 === "function") {
12
- for (let key of __getOwnPropNames(module2))
13
- if (!__hasOwnProp.call(target, key) && (copyDefault || key !== "default"))
14
- __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
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 target;
15
+ return to;
17
16
  };
18
- var __toCommonJS = /* @__PURE__ */ ((cache) => {
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 promise = this.#promises.get(binding);
44
- if (promise)
45
- return promise;
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 promise2 = Promise.resolve().then(() => factory([...stack, binding]));
49
- this.#promises.set(binding, promise2);
50
- return promise2;
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) => this.#get(binding, stack));
60
- const injections = promises ? await Promise.all(promises) : [];
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
- async inject(injectable) {
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": ";;;;;;;;;;;;;;;;;;;;;;;;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;AAAA;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/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) {
@@ -53,9 +67,16 @@ var Injector = class {
53
67
  async get(binding) {
54
68
  return this.#get(binding, []);
55
69
  }
56
- async inject(injectable) {
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
@@ -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;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: 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,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?: 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
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.2",
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.12.1",
28
- "@typescript-eslint/parser": "^5.12.1",
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.23",
32
- "eslint": "^8.9.0",
31
+ "esbuild": "^0.14.28",
32
+ "eslint": "^8.12.0",
33
33
  "eslint-config-google": "^0.14.0",
34
- "mocha": "^9.2.1",
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.5.5"
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: 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
  /**
@@ -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: 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
@@ -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
- async inject<I extends Injectable<Components, Provisions>>(
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. */