@raiyyan/brisk 0.0.0-stage → 0.1.0
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/AGENTS.md +467 -0
- package/LICENSE +21 -0
- package/README.md +1417 -2
- package/dist/bin/brisk.d.ts +3 -0
- package/dist/bin/brisk.d.ts.map +1 -0
- package/dist/bin/brisk.js +13 -0
- package/dist/bin/brisk.js.map +1 -0
- package/dist/src/cli/agents.d.ts +7 -0
- package/dist/src/cli/agents.d.ts.map +1 -0
- package/dist/src/cli/agents.js +55 -0
- package/dist/src/cli/agents.js.map +1 -0
- package/dist/src/cli/cli.d.ts +5 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +127 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/cli/client.d.ts +12 -0
- package/dist/src/cli/client.d.ts.map +1 -0
- package/dist/src/cli/client.js +76 -0
- package/dist/src/cli/client.js.map +1 -0
- package/dist/src/cli/generate.d.ts +9 -0
- package/dist/src/cli/generate.d.ts.map +1 -0
- package/dist/src/cli/generate.js +39 -0
- package/dist/src/cli/generate.js.map +1 -0
- package/dist/src/cli/init.d.ts +19 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +125 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/names.d.ts +14 -0
- package/dist/src/cli/names.d.ts.map +1 -0
- package/dist/src/cli/names.js +25 -0
- package/dist/src/cli/names.js.map +1 -0
- package/dist/src/cli/system.d.ts +35 -0
- package/dist/src/cli/system.d.ts.map +1 -0
- package/dist/src/cli/system.js +83 -0
- package/dist/src/cli/system.js.map +1 -0
- package/dist/src/cli/templates/auth.d.ts +4 -0
- package/dist/src/cli/templates/auth.d.ts.map +1 -0
- package/dist/src/cli/templates/auth.js +90 -0
- package/dist/src/cli/templates/auth.js.map +1 -0
- package/dist/src/cli/templates/database.d.ts +4 -0
- package/dist/src/cli/templates/database.d.ts.map +1 -0
- package/dist/src/cli/templates/database.js +107 -0
- package/dist/src/cli/templates/database.js.map +1 -0
- package/dist/src/cli/templates/docker.d.ts +4 -0
- package/dist/src/cli/templates/docker.d.ts.map +1 -0
- package/dist/src/cli/templates/docker.js +102 -0
- package/dist/src/cli/templates/docker.js.map +1 -0
- package/dist/src/cli/templates/feature.d.ts +8 -0
- package/dist/src/cli/templates/feature.d.ts.map +1 -0
- package/dist/src/cli/templates/feature.js +145 -0
- package/dist/src/cli/templates/feature.js.map +1 -0
- package/dist/src/cli/templates/project.d.ts +24 -0
- package/dist/src/cli/templates/project.d.ts.map +1 -0
- package/dist/src/cli/templates/project.js +166 -0
- package/dist/src/cli/templates/project.js.map +1 -0
- package/dist/src/client/client.d.ts +64 -0
- package/dist/src/client/client.d.ts.map +1 -0
- package/dist/src/client/client.js +143 -0
- package/dist/src/client/client.js.map +1 -0
- package/dist/src/client/index.d.ts +8 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +7 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/config/env.d.ts +28 -0
- package/dist/src/config/env.d.ts.map +1 -0
- package/dist/src/config/env.js +35 -0
- package/dist/src/config/env.js.map +1 -0
- package/dist/src/config/logger.d.ts +32 -0
- package/dist/src/config/logger.d.ts.map +1 -0
- package/dist/src/config/logger.js +31 -0
- package/dist/src/config/logger.js.map +1 -0
- package/dist/src/di/container.d.ts +186 -0
- package/dist/src/di/container.d.ts.map +1 -0
- package/dist/src/di/container.js +208 -0
- package/dist/src/di/container.js.map +1 -0
- package/dist/src/di/index.d.ts +8 -0
- package/dist/src/di/index.d.ts.map +1 -0
- package/dist/src/di/index.js +8 -0
- package/dist/src/di/index.js.map +1 -0
- package/dist/src/errors/http-errors.d.ts +295 -0
- package/dist/src/errors/http-errors.d.ts.map +1 -0
- package/dist/src/errors/http-errors.js +311 -0
- package/dist/src/errors/http-errors.js.map +1 -0
- package/dist/src/errors/index.d.ts +8 -0
- package/dist/src/errors/index.d.ts.map +1 -0
- package/dist/src/errors/index.js +8 -0
- package/dist/src/errors/index.js.map +1 -0
- package/dist/src/http/app.d.ts +200 -0
- package/dist/src/http/app.d.ts.map +1 -0
- package/dist/src/http/app.js +45 -0
- package/dist/src/http/app.js.map +1 -0
- package/dist/src/http/context.d.ts +66 -0
- package/dist/src/http/context.d.ts.map +1 -0
- package/dist/src/http/context.js +96 -0
- package/dist/src/http/context.js.map +1 -0
- package/dist/src/http/controller.d.ts +178 -0
- package/dist/src/http/controller.d.ts.map +1 -0
- package/dist/src/http/controller.js +317 -0
- package/dist/src/http/controller.js.map +1 -0
- package/dist/src/http/error-handler.d.ts +59 -0
- package/dist/src/http/error-handler.d.ts.map +1 -0
- package/dist/src/http/error-handler.js +124 -0
- package/dist/src/http/error-handler.js.map +1 -0
- package/dist/src/http/instance.d.ts +14 -0
- package/dist/src/http/instance.d.ts.map +1 -0
- package/dist/src/http/instance.js +309 -0
- package/dist/src/http/instance.js.map +1 -0
- package/dist/src/http/middleware.d.ts +54 -0
- package/dist/src/http/middleware.d.ts.map +1 -0
- package/dist/src/http/middleware.js +101 -0
- package/dist/src/http/middleware.js.map +1 -0
- package/dist/src/http/openapi.d.ts +34 -0
- package/dist/src/http/openapi.d.ts.map +1 -0
- package/dist/src/http/openapi.js +123 -0
- package/dist/src/http/openapi.js.map +1 -0
- package/dist/src/http/path-params.d.ts +24 -0
- package/dist/src/http/path-params.d.ts.map +1 -0
- package/dist/src/http/path-params.js +2 -0
- package/dist/src/http/path-params.js.map +1 -0
- package/dist/src/http/routes.d.ts +119 -0
- package/dist/src/http/routes.d.ts.map +1 -0
- package/dist/src/http/routes.js +2 -0
- package/dist/src/http/routes.js.map +1 -0
- package/dist/src/http/server.d.ts +23 -0
- package/dist/src/http/server.d.ts.map +1 -0
- package/dist/src/http/server.js +22 -0
- package/dist/src/http/server.js.map +1 -0
- package/dist/src/index.d.ts +22 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/testing/index.d.ts +7 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +7 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/test-client.d.ts +65 -0
- package/dist/src/testing/test-client.d.ts.map +1 -0
- package/dist/src/testing/test-client.js +77 -0
- package/dist/src/testing/test-client.js.map +1 -0
- package/package.json +88 -3
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/** A class whose constructor takes no arguments. It gets its dependencies from `inject()` in its fields. */
|
|
2
|
+
export type Class<T> = new () => T;
|
|
3
|
+
/** The key of a dependency that is not a class. Create one with {@link defineToken}. */
|
|
4
|
+
export declare class InjectionToken<T> {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly factory: (() => T) | undefined;
|
|
7
|
+
constructor(name: string, factory?: () => T);
|
|
8
|
+
toString(): string;
|
|
9
|
+
}
|
|
10
|
+
/** What `inject()` accepts: a class, which is its own key, or a token from {@link defineToken}. */
|
|
11
|
+
export type Token<T> = Class<T> | InjectionToken<T>;
|
|
12
|
+
/** A fixed value for a token, made by {@link provide}. */
|
|
13
|
+
export interface Provider<T = unknown> {
|
|
14
|
+
readonly token: Token<T>;
|
|
15
|
+
readonly value: T;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Runs at startup, before the server listens and after the `onInit` of everything this instance depends on.
|
|
19
|
+
* Use it for async setup, such as checking a database connection.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* export class Database implements OnInit, OnShutdown {
|
|
24
|
+
* client = new Pool({ connectionString: env.DATABASE_URL });
|
|
25
|
+
* async onInit() {
|
|
26
|
+
* await this.client.query("select 1");
|
|
27
|
+
* }
|
|
28
|
+
* async onShutdown() {
|
|
29
|
+
* await this.client.end();
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export interface OnInit {
|
|
35
|
+
onInit(): void | Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Runs once the server accepts requests, or once `app.start()` has started. Start background work here.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* export class ChatGateway implements OnReady {
|
|
43
|
+
* onReady() {
|
|
44
|
+
* new WebSocketServer({ server: inject(HttpServer) }).on("connection", onSocket);
|
|
45
|
+
* }
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export interface OnReady {
|
|
50
|
+
onReady(): void | Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Runs as soon as shutdown begins, while in-flight requests are still finishing. Close WebSockets and streams,
|
|
54
|
+
* and stop consumers and timers, so that no new work starts.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* export class DigestJob implements OnReady, OnDrain {
|
|
59
|
+
* #timer?: NodeJS.Timeout;
|
|
60
|
+
* onReady() {
|
|
61
|
+
* this.#timer = setInterval(() => inject(Digests).send(), 60_000);
|
|
62
|
+
* }
|
|
63
|
+
* onDrain() {
|
|
64
|
+
* clearInterval(this.#timer);
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export interface OnDrain {
|
|
70
|
+
onDrain(): void | Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Runs after the last in-flight request has finished, before the `onShutdown` of everything this instance
|
|
74
|
+
* depends on. Release pools, clients and files here.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* export class Database implements OnShutdown {
|
|
79
|
+
* readonly pool = new Pool({ connectionString: env.DATABASE_URL });
|
|
80
|
+
* async onShutdown() {
|
|
81
|
+
* await this.pool.end();
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export interface OnShutdown {
|
|
87
|
+
onShutdown(): void | Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Declares a dependency that is not a class: a config object, a client, a function, an interface
|
|
91
|
+
* implementation. With a `factory`, the value is created on first `inject()`; without one, the app must
|
|
92
|
+
* {@link provide} it.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* export const Clock = defineToken<() => Date>("Clock", () => () => new Date());
|
|
97
|
+
*
|
|
98
|
+
* const now = inject(Clock)(); // in a service
|
|
99
|
+
* const api = await createTestClient(app, { providers: [provide(Clock, () => fixedDate)] }); // in a test
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function defineToken<T>(name: string, factory?: () => T): InjectionToken<T>;
|
|
103
|
+
/**
|
|
104
|
+
* Fixes the value of a token in one app or container, replacing its factory or class.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* defineApp({ controllers, providers: [provide(Mailer, smtpMailer)] });
|
|
109
|
+
* createTestClient(app, { providers: [provide(Mailer, fakeMailer)] });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare function provide<T>(key: Token<T>, value: T): Provider<T>;
|
|
113
|
+
/**
|
|
114
|
+
* An entry of `providers`: a value from {@link provide}, or a class to create at startup even when nothing
|
|
115
|
+
* injects it, such as a WebSocket gateway or a queue consumer, so that its lifecycle hooks run.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* defineApp({ controllers, providers: [ChatGateway, provide(Clock, () => () => new Date())] });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export type ProviderEntry = Provider | Class<object>;
|
|
123
|
+
/**
|
|
124
|
+
* Returns the app's single instance for `key`, creating it on first use.
|
|
125
|
+
*
|
|
126
|
+
* Works wherever the app runs your code: class fields, token factories, controller setup, every request
|
|
127
|
+
* (`configureExpress` included, so GraphQL resolvers and tRPC procedures too), lifecycle hooks, and timers or
|
|
128
|
+
* promises started from any of these. A callback that a library fires on its own, such as a WebSocket
|
|
129
|
+
* `message` listener, may run outside the app: resolve the dependency in a field and use the field there.
|
|
130
|
+
*
|
|
131
|
+
* @throws Outside the app, for a token nothing provides, and for a dependency cycle.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* export class UsersService {
|
|
136
|
+
* readonly #users = inject(UsersRepository);
|
|
137
|
+
* readonly #log = inject(Logger);
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function inject<T>(key: Token<T>): T;
|
|
142
|
+
/**
|
|
143
|
+
* Like {@link inject}, but resolves on the first call of the returned function. Use it for a heavy client that
|
|
144
|
+
* only some code paths need, or to break a dependency cycle. The returned function works anywhere.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* export class ReportsService {
|
|
149
|
+
* readonly #pdf = injectLazy(PdfRenderer);
|
|
150
|
+
* render(report: Report) {
|
|
151
|
+
* return this.#pdf().render(report);
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function injectLazy<T>(key: Token<T>): () => T;
|
|
157
|
+
/**
|
|
158
|
+
* Holds one instance per token and runs their lifecycle hooks: `onInit` and `onReady` in creation order,
|
|
159
|
+
* dependencies first, then `onDrain` and `onShutdown` in reverse. Every app instance has one. Build one
|
|
160
|
+
* yourself to test services without HTTP.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const container = new Container([provide(Clock, () => fixedDate)]);
|
|
165
|
+
* const users = container.get(UsersService);
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export declare class Container {
|
|
169
|
+
#private;
|
|
170
|
+
constructor(providers?: readonly ProviderEntry[]);
|
|
171
|
+
/** Stores a value made outside the container, such as the server once it listens. It gets no lifecycle hooks. */
|
|
172
|
+
set<T>(key: Token<T>, value: T): void;
|
|
173
|
+
get<T>(key: Token<T>): T;
|
|
174
|
+
/**
|
|
175
|
+
* Runs `fn` with this container as the target of `inject()`, across its awaits too. Costs nothing when the
|
|
176
|
+
* caller is already inside this container, as every request is.
|
|
177
|
+
*/
|
|
178
|
+
run<R, A extends unknown[]>(fn: (...args: A) => R, ...args: A): R;
|
|
179
|
+
init(): Promise<void>;
|
|
180
|
+
ready(): Promise<void>;
|
|
181
|
+
/** Calls every `onDrain` in reverse creation order, even when some fail. */
|
|
182
|
+
drain(onError: (error: unknown, owner: string) => void): Promise<void>;
|
|
183
|
+
/** Calls every `onShutdown` in reverse creation order, even when some fail; each failure goes to `onError`. */
|
|
184
|
+
shutdown(onError: (error: unknown, owner: string) => void): Promise<void>;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=container.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../src/di/container.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
/** The key of a dependency that is not a class. Create one with {@link defineToken}. */
|
|
3
|
+
export class InjectionToken {
|
|
4
|
+
name;
|
|
5
|
+
factory;
|
|
6
|
+
constructor(name, factory) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.factory = factory;
|
|
9
|
+
}
|
|
10
|
+
toString() {
|
|
11
|
+
return this.name;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Declares a dependency that is not a class: a config object, a client, a function, an interface
|
|
16
|
+
* implementation. With a `factory`, the value is created on first `inject()`; without one, the app must
|
|
17
|
+
* {@link provide} it.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* export const Clock = defineToken<() => Date>("Clock", () => () => new Date());
|
|
22
|
+
*
|
|
23
|
+
* const now = inject(Clock)(); // in a service
|
|
24
|
+
* const api = await createTestClient(app, { providers: [provide(Clock, () => fixedDate)] }); // in a test
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function defineToken(name, factory) {
|
|
28
|
+
return new InjectionToken(name, factory);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fixes the value of a token in one app or container, replacing its factory or class.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* defineApp({ controllers, providers: [provide(Mailer, smtpMailer)] });
|
|
36
|
+
* createTestClient(app, { providers: [provide(Mailer, fakeMailer)] });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function provide(key, value) {
|
|
40
|
+
return { token: key, value };
|
|
41
|
+
}
|
|
42
|
+
const scope = new AsyncLocalStorage();
|
|
43
|
+
/**
|
|
44
|
+
* Returns the app's single instance for `key`, creating it on first use.
|
|
45
|
+
*
|
|
46
|
+
* Works wherever the app runs your code: class fields, token factories, controller setup, every request
|
|
47
|
+
* (`configureExpress` included, so GraphQL resolvers and tRPC procedures too), lifecycle hooks, and timers or
|
|
48
|
+
* promises started from any of these. A callback that a library fires on its own, such as a WebSocket
|
|
49
|
+
* `message` listener, may run outside the app: resolve the dependency in a field and use the field there.
|
|
50
|
+
*
|
|
51
|
+
* @throws Outside the app, for a token nothing provides, and for a dependency cycle.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* export class UsersService {
|
|
56
|
+
* readonly #users = inject(UsersRepository);
|
|
57
|
+
* readonly #log = inject(Logger);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function inject(key) {
|
|
62
|
+
return currentContainer(`inject(${nameOf(key)})`).get(key);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Like {@link inject}, but resolves on the first call of the returned function. Use it for a heavy client that
|
|
66
|
+
* only some code paths need, or to break a dependency cycle. The returned function works anywhere.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* export class ReportsService {
|
|
71
|
+
* readonly #pdf = injectLazy(PdfRenderer);
|
|
72
|
+
* render(report: Report) {
|
|
73
|
+
* return this.#pdf().render(report);
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function injectLazy(key) {
|
|
79
|
+
const container = currentContainer(`injectLazy(${nameOf(key)})`);
|
|
80
|
+
return () => container.get(key);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The container `inject()` uses: the one running the current request, setup or hook. There is deliberately no
|
|
84
|
+
* process-wide fallback: guessing "the" app works with one app and fails with two, so code would pass alone
|
|
85
|
+
* and break next to another test file.
|
|
86
|
+
*/
|
|
87
|
+
function currentContainer(caller) {
|
|
88
|
+
const container = scope.getStore();
|
|
89
|
+
if (container)
|
|
90
|
+
return container;
|
|
91
|
+
throw new Error(`${caller} was called outside the app. Call inject() in a class field, a token factory, a controller setup, ` +
|
|
92
|
+
"a request or a lifecycle hook. In a callback a library fires on its own (a WebSocket message, a queue " +
|
|
93
|
+
"handler), use a dependency the class resolved in a field, or app.container.get(...).");
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Holds one instance per token and runs their lifecycle hooks: `onInit` and `onReady` in creation order,
|
|
97
|
+
* dependencies first, then `onDrain` and `onShutdown` in reverse. Every app instance has one. Build one
|
|
98
|
+
* yourself to test services without HTTP.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const container = new Container([provide(Clock, () => fixedDate)]);
|
|
103
|
+
* const users = container.get(UsersService);
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export class Container {
|
|
107
|
+
#instances = new Map();
|
|
108
|
+
#ordered = [];
|
|
109
|
+
#resolving = [];
|
|
110
|
+
constructor(providers = []) {
|
|
111
|
+
const eager = [];
|
|
112
|
+
for (const entry of providers) {
|
|
113
|
+
if (typeof entry === "function") {
|
|
114
|
+
eager.push(entry);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (!this.#instances.has(entry.token))
|
|
118
|
+
this.#ordered.push(entry.value);
|
|
119
|
+
this.#instances.set(entry.token, entry.value);
|
|
120
|
+
}
|
|
121
|
+
for (const cls of eager)
|
|
122
|
+
this.get(cls);
|
|
123
|
+
}
|
|
124
|
+
/** Stores a value made outside the container, such as the server once it listens. It gets no lifecycle hooks. */
|
|
125
|
+
set(key, value) {
|
|
126
|
+
this.#instances.set(key, value);
|
|
127
|
+
}
|
|
128
|
+
get(key) {
|
|
129
|
+
if (this.#instances.has(key)) {
|
|
130
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- every entry is stored under its own key
|
|
131
|
+
return this.#instances.get(key);
|
|
132
|
+
}
|
|
133
|
+
if (this.#resolving.includes(key)) {
|
|
134
|
+
throw new Error(`Circular dependency: ${[...this.#resolving, key].map(nameOf).join(" -> ")}`);
|
|
135
|
+
}
|
|
136
|
+
this.#resolving.push(key);
|
|
137
|
+
try {
|
|
138
|
+
const instance = this.run(() => create(key));
|
|
139
|
+
this.#instances.set(key, instance);
|
|
140
|
+
this.#ordered.push(instance);
|
|
141
|
+
return instance;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
this.#resolving.pop();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Runs `fn` with this container as the target of `inject()`, across its awaits too. Costs nothing when the
|
|
149
|
+
* caller is already inside this container, as every request is.
|
|
150
|
+
*/
|
|
151
|
+
run(fn, ...args) {
|
|
152
|
+
return scope.getStore() === this ? fn(...args) : scope.run(this, fn, ...args);
|
|
153
|
+
}
|
|
154
|
+
async init() {
|
|
155
|
+
// oxlint-disable-next-line no-await-in-loop -- lifecycle hooks run one at a time, in dependency order
|
|
156
|
+
for (const hook of this.#hooks("onInit", this.#ordered))
|
|
157
|
+
await hook.run();
|
|
158
|
+
}
|
|
159
|
+
async ready() {
|
|
160
|
+
// oxlint-disable-next-line no-await-in-loop -- lifecycle hooks run one at a time, in dependency order
|
|
161
|
+
for (const hook of this.#hooks("onReady", this.#ordered))
|
|
162
|
+
await hook.run();
|
|
163
|
+
}
|
|
164
|
+
/** Calls every `onDrain` in reverse creation order, even when some fail. */
|
|
165
|
+
drain(onError) {
|
|
166
|
+
return this.#runAll("onDrain", onError);
|
|
167
|
+
}
|
|
168
|
+
/** Calls every `onShutdown` in reverse creation order, even when some fail; each failure goes to `onError`. */
|
|
169
|
+
shutdown(onError) {
|
|
170
|
+
return this.#runAll("onShutdown", onError);
|
|
171
|
+
}
|
|
172
|
+
async #runAll(name, onError) {
|
|
173
|
+
for (const hook of this.#hooks(name, this.#ordered.toReversed())) {
|
|
174
|
+
try {
|
|
175
|
+
// oxlint-disable-next-line no-await-in-loop -- dependents shut down before their dependencies
|
|
176
|
+
await hook.run();
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
onError(error, hook.owner);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Snapshots the instances that define `name`, bound to run inside this container. */
|
|
184
|
+
#hooks(name, instances) {
|
|
185
|
+
return instances.flatMap((instance) => {
|
|
186
|
+
if (typeof instance !== "object" || instance === null)
|
|
187
|
+
return [];
|
|
188
|
+
const hook = Reflect.get(instance, name);
|
|
189
|
+
if (!isHook(hook))
|
|
190
|
+
return [];
|
|
191
|
+
return [{ owner: instance.constructor.name, run: async () => this.run(() => hook.call(instance)) }];
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function create(key) {
|
|
196
|
+
if (!(key instanceof InjectionToken))
|
|
197
|
+
return new key();
|
|
198
|
+
if (!key.factory)
|
|
199
|
+
throw new Error(`No provider for ${key.name}: pass provide(${key.name}, value) to the app`);
|
|
200
|
+
return key.factory();
|
|
201
|
+
}
|
|
202
|
+
function isHook(value) {
|
|
203
|
+
return typeof value === "function";
|
|
204
|
+
}
|
|
205
|
+
function nameOf(key) {
|
|
206
|
+
return key.name;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../../src/di/container.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency injection on its own: tokens, `inject()`, providers, the container and lifecycle interfaces.
|
|
3
|
+
* Everything here is also exported from `@raiyyan/brisk`.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export { Container, defineToken, inject, injectLazy, provide, type Class, type InjectionToken, type OnDrain, type OnInit, type OnReady, type OnShutdown, type Provider, type ProviderEntry, type Token, } from "./container.ts";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/di/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,SAAS,EACT,WAAW,EACX,MAAM,EACN,UAAU,EACV,OAAO,EACP,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,KAAK,MAAM,EACX,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,KAAK,GACX,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency injection on its own: tokens, `inject()`, providers, the container and lifecycle interfaces.
|
|
3
|
+
* Everything here is also exported from `@raiyyan/brisk`.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export { Container, defineToken, inject, injectLazy, provide, } from "./container.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/di/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,SAAS,EACT,WAAW,EACX,MAAM,EACN,UAAU,EACV,OAAO,GAUR,MAAM,gBAAgB,CAAC"}
|