@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.
Files changed (140) hide show
  1. package/AGENTS.md +467 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1417 -2
  4. package/dist/bin/brisk.d.ts +3 -0
  5. package/dist/bin/brisk.d.ts.map +1 -0
  6. package/dist/bin/brisk.js +13 -0
  7. package/dist/bin/brisk.js.map +1 -0
  8. package/dist/src/cli/agents.d.ts +7 -0
  9. package/dist/src/cli/agents.d.ts.map +1 -0
  10. package/dist/src/cli/agents.js +55 -0
  11. package/dist/src/cli/agents.js.map +1 -0
  12. package/dist/src/cli/cli.d.ts +5 -0
  13. package/dist/src/cli/cli.d.ts.map +1 -0
  14. package/dist/src/cli/cli.js +127 -0
  15. package/dist/src/cli/cli.js.map +1 -0
  16. package/dist/src/cli/client.d.ts +12 -0
  17. package/dist/src/cli/client.d.ts.map +1 -0
  18. package/dist/src/cli/client.js +76 -0
  19. package/dist/src/cli/client.js.map +1 -0
  20. package/dist/src/cli/generate.d.ts +9 -0
  21. package/dist/src/cli/generate.d.ts.map +1 -0
  22. package/dist/src/cli/generate.js +39 -0
  23. package/dist/src/cli/generate.js.map +1 -0
  24. package/dist/src/cli/init.d.ts +19 -0
  25. package/dist/src/cli/init.d.ts.map +1 -0
  26. package/dist/src/cli/init.js +125 -0
  27. package/dist/src/cli/init.js.map +1 -0
  28. package/dist/src/cli/names.d.ts +14 -0
  29. package/dist/src/cli/names.d.ts.map +1 -0
  30. package/dist/src/cli/names.js +25 -0
  31. package/dist/src/cli/names.js.map +1 -0
  32. package/dist/src/cli/system.d.ts +35 -0
  33. package/dist/src/cli/system.d.ts.map +1 -0
  34. package/dist/src/cli/system.js +83 -0
  35. package/dist/src/cli/system.js.map +1 -0
  36. package/dist/src/cli/templates/auth.d.ts +4 -0
  37. package/dist/src/cli/templates/auth.d.ts.map +1 -0
  38. package/dist/src/cli/templates/auth.js +90 -0
  39. package/dist/src/cli/templates/auth.js.map +1 -0
  40. package/dist/src/cli/templates/database.d.ts +4 -0
  41. package/dist/src/cli/templates/database.d.ts.map +1 -0
  42. package/dist/src/cli/templates/database.js +107 -0
  43. package/dist/src/cli/templates/database.js.map +1 -0
  44. package/dist/src/cli/templates/docker.d.ts +4 -0
  45. package/dist/src/cli/templates/docker.d.ts.map +1 -0
  46. package/dist/src/cli/templates/docker.js +102 -0
  47. package/dist/src/cli/templates/docker.js.map +1 -0
  48. package/dist/src/cli/templates/feature.d.ts +8 -0
  49. package/dist/src/cli/templates/feature.d.ts.map +1 -0
  50. package/dist/src/cli/templates/feature.js +145 -0
  51. package/dist/src/cli/templates/feature.js.map +1 -0
  52. package/dist/src/cli/templates/project.d.ts +24 -0
  53. package/dist/src/cli/templates/project.d.ts.map +1 -0
  54. package/dist/src/cli/templates/project.js +166 -0
  55. package/dist/src/cli/templates/project.js.map +1 -0
  56. package/dist/src/client/client.d.ts +64 -0
  57. package/dist/src/client/client.d.ts.map +1 -0
  58. package/dist/src/client/client.js +143 -0
  59. package/dist/src/client/client.js.map +1 -0
  60. package/dist/src/client/index.d.ts +8 -0
  61. package/dist/src/client/index.d.ts.map +1 -0
  62. package/dist/src/client/index.js +7 -0
  63. package/dist/src/client/index.js.map +1 -0
  64. package/dist/src/config/env.d.ts +28 -0
  65. package/dist/src/config/env.d.ts.map +1 -0
  66. package/dist/src/config/env.js +35 -0
  67. package/dist/src/config/env.js.map +1 -0
  68. package/dist/src/config/logger.d.ts +32 -0
  69. package/dist/src/config/logger.d.ts.map +1 -0
  70. package/dist/src/config/logger.js +31 -0
  71. package/dist/src/config/logger.js.map +1 -0
  72. package/dist/src/di/container.d.ts +186 -0
  73. package/dist/src/di/container.d.ts.map +1 -0
  74. package/dist/src/di/container.js +208 -0
  75. package/dist/src/di/container.js.map +1 -0
  76. package/dist/src/di/index.d.ts +8 -0
  77. package/dist/src/di/index.d.ts.map +1 -0
  78. package/dist/src/di/index.js +8 -0
  79. package/dist/src/di/index.js.map +1 -0
  80. package/dist/src/errors/http-errors.d.ts +295 -0
  81. package/dist/src/errors/http-errors.d.ts.map +1 -0
  82. package/dist/src/errors/http-errors.js +311 -0
  83. package/dist/src/errors/http-errors.js.map +1 -0
  84. package/dist/src/errors/index.d.ts +8 -0
  85. package/dist/src/errors/index.d.ts.map +1 -0
  86. package/dist/src/errors/index.js +8 -0
  87. package/dist/src/errors/index.js.map +1 -0
  88. package/dist/src/http/app.d.ts +200 -0
  89. package/dist/src/http/app.d.ts.map +1 -0
  90. package/dist/src/http/app.js +45 -0
  91. package/dist/src/http/app.js.map +1 -0
  92. package/dist/src/http/context.d.ts +66 -0
  93. package/dist/src/http/context.d.ts.map +1 -0
  94. package/dist/src/http/context.js +96 -0
  95. package/dist/src/http/context.js.map +1 -0
  96. package/dist/src/http/controller.d.ts +178 -0
  97. package/dist/src/http/controller.d.ts.map +1 -0
  98. package/dist/src/http/controller.js +317 -0
  99. package/dist/src/http/controller.js.map +1 -0
  100. package/dist/src/http/error-handler.d.ts +59 -0
  101. package/dist/src/http/error-handler.d.ts.map +1 -0
  102. package/dist/src/http/error-handler.js +124 -0
  103. package/dist/src/http/error-handler.js.map +1 -0
  104. package/dist/src/http/instance.d.ts +14 -0
  105. package/dist/src/http/instance.d.ts.map +1 -0
  106. package/dist/src/http/instance.js +309 -0
  107. package/dist/src/http/instance.js.map +1 -0
  108. package/dist/src/http/middleware.d.ts +54 -0
  109. package/dist/src/http/middleware.d.ts.map +1 -0
  110. package/dist/src/http/middleware.js +101 -0
  111. package/dist/src/http/middleware.js.map +1 -0
  112. package/dist/src/http/openapi.d.ts +34 -0
  113. package/dist/src/http/openapi.d.ts.map +1 -0
  114. package/dist/src/http/openapi.js +123 -0
  115. package/dist/src/http/openapi.js.map +1 -0
  116. package/dist/src/http/path-params.d.ts +24 -0
  117. package/dist/src/http/path-params.d.ts.map +1 -0
  118. package/dist/src/http/path-params.js +2 -0
  119. package/dist/src/http/path-params.js.map +1 -0
  120. package/dist/src/http/routes.d.ts +119 -0
  121. package/dist/src/http/routes.d.ts.map +1 -0
  122. package/dist/src/http/routes.js +2 -0
  123. package/dist/src/http/routes.js.map +1 -0
  124. package/dist/src/http/server.d.ts +23 -0
  125. package/dist/src/http/server.d.ts.map +1 -0
  126. package/dist/src/http/server.js +22 -0
  127. package/dist/src/http/server.js.map +1 -0
  128. package/dist/src/index.d.ts +22 -0
  129. package/dist/src/index.d.ts.map +1 -0
  130. package/dist/src/index.js +19 -0
  131. package/dist/src/index.js.map +1 -0
  132. package/dist/src/testing/index.d.ts +7 -0
  133. package/dist/src/testing/index.d.ts.map +1 -0
  134. package/dist/src/testing/index.js +7 -0
  135. package/dist/src/testing/index.js.map +1 -0
  136. package/dist/src/testing/test-client.d.ts +65 -0
  137. package/dist/src/testing/test-client.d.ts.map +1 -0
  138. package/dist/src/testing/test-client.js +77 -0
  139. package/dist/src/testing/test-client.js.map +1 -0
  140. 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"}