Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1/* eslint-disable max-lines */
2/* eslint-disable @typescript-eslint/no-explicit-any */
3import type Bottle from '@ethanresnick/bottlejs';
4
5import { __throw } from '../utils/misc.js';
6import { jsonStringify } from '../utils/encoding.js';
7import { type Dependencies as Deps } from './index.js';
8
9const DEPENDENCIES = Symbol();
10type DepName = keyof Deps;
11
12export type Factory<D extends any[], R extends any> =
13 | ((...args: D) => R)
14 | (new (...args: D) => R);
15
16export type AnnotatedFactory<ServiceType> = Factory<any[], ServiceType> & {
17 [DEPENDENCIES]: DepName[];
18};
19
20/**
21 * Inject is used to define the dependencies that a service (or other injectable
22 * value) will need in order to create the service. It has a ton of overloads
23 * to support static typing for the injected dependencies based on their name.
24 *
25 * @param deps A list of dependency names. These dictate the dependencies to
26 * inject when the container calls factory.
27 *
28 * @param factory A function that will return the value (to be stored in the
29 * container) for a service/injectable. This factory function will be called
30 * with instances of the dependencies specified in `deps`. A class can also be
31 * passed here, as (ofc) classes in js are runtime values represented by their
32 * constructor function, which works as the factory.
33 *
34 * @returns A version of the factory function that can be passed to
35 * {@link register}, and that will be set up with bottle to have the right
36 * dependencies injected.
37 */
38export function inject<F extends Factory<[], any>>(
39 deps: [],
40 factory: F,
41): F & { [DEPENDENCIES]: [] };
42export function inject<T extends DepName, F extends Factory<[Deps[T]], any>>(
43 deps: [T],
44 factory: F,
45): F & { [DEPENDENCIES]: [T] };
46export function inject<
47 T extends DepName,
48 U extends DepName,
49 F extends Factory<[Deps[T], Deps[U]], any>,
50>(deps: [T, U], factory: F): F & { [DEPENDENCIES]: [T, U] };
51export function inject<
52 T extends DepName,
53 U extends DepName,
54 V extends DepName,
55 F extends Factory<[Deps[T], Deps[U], Deps[V]], any>,
56>(deps: [T, U, V], factory: F): F & { [DEPENDENCIES]: [T, U, V] };
57export function inject<
58 T extends DepName,
59 U extends DepName,
60 V extends DepName,
61 W extends DepName,
62 F extends Factory<[Deps[T], Deps[U], Deps[V], Deps[W]], any>,
63>(deps: [T, U, V, W], factory: F): F & { [DEPENDENCIES]: [T, U, V, W] };
64export function inject<
65 T extends DepName,
66 U extends DepName,
67 V extends DepName,
68 W extends DepName,
69 X extends DepName,
70 F extends Factory<[Deps[T], Deps[U], Deps[V], Deps[W], Deps[X]], any>,
71>(deps: [T, U, V, W, X], factory: F): F & { [DEPENDENCIES]: [T, U, V, W, X] };
72export function inject<
73 T extends DepName,
74 U extends DepName,
75 V extends DepName,
76 W extends DepName,
77 X extends DepName,
78 Y extends DepName,
79 F extends Factory<
80 [Deps[T], Deps[U], Deps[V], Deps[W], Deps[X], Deps[Y]],
81 any
82 >,
83>(
84 deps: [T, U, V, W, X, Y],
85 factory: F,
86): F & { [DEPENDENCIES]: [T, U, V, W, X, Y] };
87export function inject<
88 T extends DepName,
89 U extends DepName,
90 V extends DepName,
91 W extends DepName,
92 X extends DepName,
93 Y extends DepName,
94 Z extends DepName,
95 F extends Factory<
96 [Deps[T], Deps[U], Deps[V], Deps[W], Deps[X], Deps[Y], Deps[Z]],
97 any
98 >,
99>(
100 deps: [T, U, V, W, X, Y, Z],
101 factory: F,
102): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z] };
103export function inject<
104 T extends DepName,
105 U extends DepName,
106 V extends DepName,
107 W extends DepName,
108 X extends DepName,
109 Y extends DepName,
110 Z extends DepName,
111 S extends DepName,
112 F extends Factory<
113 [Deps[T], Deps[U], Deps[V], Deps[W], Deps[X], Deps[Y], Deps[Z], Deps[S]],
114 any
115 >,
116>(
117 deps: [T, U, V, W, X, Y, Z, S],
118 factory: F,
119): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S] };
120export function inject<
121 T extends DepName,
122 U extends DepName,
123 V extends DepName,
124 W extends DepName,
125 X extends DepName,
126 Y extends DepName,
127 Z extends DepName,
128 S extends DepName,
129 R extends DepName,
130 F extends Factory<
131 [
132 Deps[T],
133 Deps[U],
134 Deps[V],
135 Deps[W],
136 Deps[X],
137 Deps[Y],
138 Deps[Z],
139 Deps[S],
140 Deps[R],
141 ],
142 any
143 >,
144>(
145 deps: [T, U, V, W, X, Y, Z, S, R],
146 factory: F,
147): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R] };
148export function inject<
149 T extends DepName,
150 U extends DepName,
151 V extends DepName,
152 W extends DepName,
153 X extends DepName,
154 Y extends DepName,
155 Z extends DepName,
156 S extends DepName,
157 R extends DepName,
158 Q extends DepName,
159 F extends Factory<
160 [
161 Deps[T],
162 Deps[U],
163 Deps[V],
164 Deps[W],
165 Deps[X],
166 Deps[Y],
167 Deps[Z],
168 Deps[S],
169 Deps[R],
170 Deps[Q],
171 ],
172 any
173 >,
174>(
175 deps: [T, U, V, W, X, Y, Z, S, R, Q],
176 factory: F,
177): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q] };
178export function inject<
179 T extends DepName,
180 U extends DepName,
181 V extends DepName,
182 W extends DepName,
183 X extends DepName,
184 Y extends DepName,
185 Z extends DepName,
186 S extends DepName,
187 R extends DepName,
188 Q extends DepName,
189 P extends DepName,
190 F extends Factory<
191 [
192 Deps[T],
193 Deps[U],
194 Deps[V],
195 Deps[W],
196 Deps[X],
197 Deps[Y],
198 Deps[Z],
199 Deps[S],
200 Deps[R],
201 Deps[Q],
202 Deps[P],
203 ],
204 any
205 >,
206>(
207 deps: [T, U, V, W, X, Y, Z, S, R, Q, P],
208 factory: F,
209): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P] };
210export function inject<
211 T extends DepName,
212 U extends DepName,
213 V extends DepName,
214 W extends DepName,
215 X extends DepName,
216 Y extends DepName,
217 Z extends DepName,
218 S extends DepName,
219 R extends DepName,
220 Q extends DepName,
221 P extends DepName,
222 O extends DepName,
223 F extends Factory<
224 [
225 Deps[T],
226 Deps[U],
227 Deps[V],
228 Deps[W],
229 Deps[X],
230 Deps[Y],
231 Deps[Z],
232 Deps[S],
233 Deps[R],
234 Deps[Q],
235 Deps[P],
236 Deps[O],
237 ],
238 any
239 >,
240>(
241 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O],
242 factory: F,
243): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O] };
244export function inject<
245 T extends DepName,
246 U extends DepName,
247 V extends DepName,
248 W extends DepName,
249 X extends DepName,
250 Y extends DepName,
251 Z extends DepName,
252 S extends DepName,
253 R extends DepName,
254 Q extends DepName,
255 P extends DepName,
256 O extends DepName,
257 N extends DepName,
258 F extends Factory<
259 [
260 Deps[T],
261 Deps[U],
262 Deps[V],
263 Deps[W],
264 Deps[X],
265 Deps[Y],
266 Deps[Z],
267 Deps[S],
268 Deps[R],
269 Deps[Q],
270 Deps[P],
271 Deps[O],
272 Deps[N],
273 ],
274 any
275 >,
276>(
277 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N],
278 factory: F,
279): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N] };
280export function inject<
281 T extends DepName,
282 U extends DepName,
283 V extends DepName,
284 W extends DepName,
285 X extends DepName,
286 Y extends DepName,
287 Z extends DepName,
288 S extends DepName,
289 R extends DepName,
290 Q extends DepName,
291 P extends DepName,
292 O extends DepName,
293 N extends DepName,
294 M extends DepName,
295 F extends Factory<
296 [
297 Deps[T],
298 Deps[U],
299 Deps[V],
300 Deps[W],
301 Deps[X],
302 Deps[Y],
303 Deps[Z],
304 Deps[S],
305 Deps[R],
306 Deps[Q],
307 Deps[P],
308 Deps[O],
309 Deps[N],
310 Deps[M],
311 ],
312 any
313 >,
314>(
315 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M],
316 factory: F,
317): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M] };
318export function inject<
319 T extends DepName,
320 U extends DepName,
321 V extends DepName,
322 W extends DepName,
323 X extends DepName,
324 Y extends DepName,
325 Z extends DepName,
326 S extends DepName,
327 R extends DepName,
328 Q extends DepName,
329 P extends DepName,
330 O extends DepName,
331 N extends DepName,
332 M extends DepName,
333 L extends DepName,
334 F extends Factory<
335 [
336 Deps[T],
337 Deps[U],
338 Deps[V],
339 Deps[W],
340 Deps[X],
341 Deps[Y],
342 Deps[Z],
343 Deps[S],
344 Deps[R],
345 Deps[Q],
346 Deps[P],
347 Deps[O],
348 Deps[N],
349 Deps[M],
350 Deps[L],
351 ],
352 any
353 >,
354>(
355 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L],
356 factory: F,
357): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L] };
358export function inject<
359 T extends DepName,
360 U extends DepName,
361 V extends DepName,
362 W extends DepName,
363 X extends DepName,
364 Y extends DepName,
365 Z extends DepName,
366 S extends DepName,
367 R extends DepName,
368 Q extends DepName,
369 P extends DepName,
370 O extends DepName,
371 N extends DepName,
372 M extends DepName,
373 L extends DepName,
374 K extends DepName,
375 F extends Factory<
376 [
377 Deps[T],
378 Deps[U],
379 Deps[V],
380 Deps[W],
381 Deps[X],
382 Deps[Y],
383 Deps[Z],
384 Deps[S],
385 Deps[R],
386 Deps[Q],
387 Deps[P],
388 Deps[O],
389 Deps[N],
390 Deps[M],
391 Deps[L],
392 Deps[K],
393 ],
394 any
395 >,
396>(
397 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K],
398 factory: F,
399): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K] };
400export function inject<
401 T extends DepName,
402 U extends DepName,
403 V extends DepName,
404 W extends DepName,
405 X extends DepName,
406 Y extends DepName,
407 Z extends DepName,
408 S extends DepName,
409 R extends DepName,
410 Q extends DepName,
411 P extends DepName,
412 O extends DepName,
413 N extends DepName,
414 M extends DepName,
415 L extends DepName,
416 K extends DepName,
417 J extends DepName,
418 F extends Factory<
419 [
420 Deps[T],
421 Deps[U],
422 Deps[V],
423 Deps[W],
424 Deps[X],
425 Deps[Y],
426 Deps[Z],
427 Deps[S],
428 Deps[R],
429 Deps[Q],
430 Deps[P],
431 Deps[O],
432 Deps[N],
433 Deps[M],
434 Deps[L],
435 Deps[K],
436 Deps[J],
437 ],
438 any
439 >,
440>(
441 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J],
442 factory: F,
443): F & { [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J] };
444
445export function inject<
446 T extends DepName,
447 U extends DepName,
448 V extends DepName,
449 W extends DepName,
450 X extends DepName,
451 Y extends DepName,
452 Z extends DepName,
453 S extends DepName,
454 R extends DepName,
455 Q extends DepName,
456 P extends DepName,
457 O extends DepName,
458 N extends DepName,
459 M extends DepName,
460 L extends DepName,
461 K extends DepName,
462 J extends DepName,
463 I extends DepName,
464 F extends Factory<
465 [
466 Deps[T],
467 Deps[U],
468 Deps[V],
469 Deps[W],
470 Deps[X],
471 Deps[Y],
472 Deps[Z],
473 Deps[S],
474 Deps[R],
475 Deps[Q],
476 Deps[P],
477 Deps[O],
478 Deps[N],
479 Deps[M],
480 Deps[L],
481 Deps[K],
482 Deps[J],
483 Deps[I],
484 ],
485 any
486 >,
487>(
488 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J, I],
489 factory: F,
490): F & {
491 [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J, I];
492};
493export function inject<
494 T extends DepName,
495 U extends DepName,
496 V extends DepName,
497 W extends DepName,
498 X extends DepName,
499 Y extends DepName,
500 Z extends DepName,
501 S extends DepName,
502 R extends DepName,
503 Q extends DepName,
504 P extends DepName,
505 O extends DepName,
506 N extends DepName,
507 M extends DepName,
508 L extends DepName,
509 K extends DepName,
510 J extends DepName,
511 I extends DepName,
512 H extends DepName,
513 G extends DepName,
514 F extends Factory<
515 [
516 Deps[T],
517 Deps[U],
518 Deps[V],
519 Deps[W],
520 Deps[X],
521 Deps[Y],
522 Deps[Z],
523 Deps[S],
524 Deps[R],
525 Deps[Q],
526 Deps[P],
527 Deps[O],
528 Deps[N],
529 Deps[M],
530 Deps[L],
531 Deps[K],
532 Deps[J],
533 Deps[I],
534 Deps[H],
535 Deps[G],
536 ],
537 any
538 >,
539>(
540 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J, I, H, G],
541 factory: F,
542): F & {
543 [DEPENDENCIES]: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J, I, H, G];
544};
545
546export function inject<
547 T extends DepName,
548 U extends DepName,
549 V extends DepName,
550 W extends DepName,
551 X extends DepName,
552 Y extends DepName,
553 Z extends DepName,
554 S extends DepName,
555 R extends DepName,
556 Q extends DepName,
557 P extends DepName,
558 O extends DepName,
559 N extends DepName,
560 M extends DepName,
561 L extends DepName,
562 K extends DepName,
563 J extends DepName,
564 I extends DepName,
565 H extends DepName,
566 G extends DepName,
567 // no F
568 E extends DepName,
569 F extends Factory<
570 [
571 Deps[T],
572 Deps[U],
573 Deps[V],
574 Deps[W],
575 Deps[X],
576 Deps[Y],
577 Deps[Z],
578 Deps[S],
579 Deps[R],
580 Deps[Q],
581 Deps[P],
582 Deps[O],
583 Deps[N],
584 Deps[M],
585 Deps[L],
586 Deps[K],
587 Deps[J],
588 Deps[I],
589 Deps[H],
590 Deps[G],
591 Deps[E],
592 ],
593 any
594 >,
595>(
596 deps: [T, U, V, W, X, Y, Z, S, R, Q, P, O, N, M, L, K, J, I, H, G, E],
597 factory: F,
598): F & {
599 [DEPENDENCIES]: [
600 T,
601 U,
602 V,
603 W,
604 X,
605 Y,
606 Z,
607 S,
608 R,
609 Q,
610 P,
611 O,
612 N,
613 M,
614 L,
615 K,
616 J,
617 I,
618 H,
619 G,
620 E,
621 ];
622};
623export function inject<F extends (...it: any[]) => any>(
624 deps: DepName[],
625 factory: F,
626): F & { [DEPENDENCIES]: DepName[] } {
627 (factory as any)[DEPENDENCIES] = deps;
628 return factory as any;
629}
630
631export function register<T extends keyof Deps & string>(
632 bottle: Bottle<Deps>,
633 name: T,
634 factory: AnnotatedFactory<Deps[T]>,
635) {
636 const deps = factory[DEPENDENCIES];
637 if (isConstructable(factory)) {
638 bottle.service<T>(name, factory, ...deps);
639 } else {
640 bottle.serviceFactory(name, factory, ...deps);
641 }
642}
643
644// See https://stackoverflow.com/a/49510834/1261879
645function isConstructable(fn: any): fn is new (...args: any[]) => any {
646 try {
647 // eslint-disable-next-line no-new
648 new new Proxy(fn, { construct: () => ({}) })();
649 return true;
650 } catch (err) {
651 return false;
652 }
653}
654
655/**
656 * Gets an env var, or throws if the variable is undefined. This is critical to
657 * make the app hard crash early (so we'll get alerts) if some expected config
658 * var is missing.
659 */
660export function safeGetEnvVar(varName: string): string {
661 return (
662 process.env[varName] ?? __throw(new Error(`Missing env var ${varName}`))
663 );
664}
665
666/**
667 * Gets an env var and parses it as a positive integer. Returns `defaultValue`
668 * if the variable is unset or invalid, logging an error on misconfiguration.
669 */
670export function safeGetEnvInt(varName: string, defaultValue: number): number {
671 const raw = process.env[varName];
672 if (raw === undefined) return defaultValue;
673 const parsed = parseInt(raw, 10);
674 if (!Number.isInteger(parsed) || parsed <= 0) {
675 // eslint-disable-next-line no-console
676 console.error(
677 `Invalid env var ${varName}: expected a positive integer, got ${jsonStringify(raw)}. Using default value ${defaultValue}.`,
678 );
679 return defaultValue;
680 }
681 return parsed;
682}