@buenojs/bueno 0.8.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 (120) hide show
  1. package/.env.example +109 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/LICENSE +21 -0
  4. package/README.md +892 -0
  5. package/architecture.md +652 -0
  6. package/bun.lock +70 -0
  7. package/dist/cli/index.js +3233 -0
  8. package/dist/index.js +9014 -0
  9. package/package.json +77 -0
  10. package/src/cache/index.ts +795 -0
  11. package/src/cli/ARCHITECTURE.md +837 -0
  12. package/src/cli/bin.ts +10 -0
  13. package/src/cli/commands/build.ts +425 -0
  14. package/src/cli/commands/dev.ts +248 -0
  15. package/src/cli/commands/generate.ts +541 -0
  16. package/src/cli/commands/help.ts +55 -0
  17. package/src/cli/commands/index.ts +112 -0
  18. package/src/cli/commands/migration.ts +355 -0
  19. package/src/cli/commands/new.ts +804 -0
  20. package/src/cli/commands/start.ts +208 -0
  21. package/src/cli/core/args.ts +283 -0
  22. package/src/cli/core/console.ts +349 -0
  23. package/src/cli/core/index.ts +60 -0
  24. package/src/cli/core/prompt.ts +424 -0
  25. package/src/cli/core/spinner.ts +265 -0
  26. package/src/cli/index.ts +135 -0
  27. package/src/cli/templates/deploy.ts +295 -0
  28. package/src/cli/templates/docker.ts +307 -0
  29. package/src/cli/templates/index.ts +24 -0
  30. package/src/cli/utils/fs.ts +428 -0
  31. package/src/cli/utils/index.ts +8 -0
  32. package/src/cli/utils/strings.ts +197 -0
  33. package/src/config/env.ts +408 -0
  34. package/src/config/index.ts +506 -0
  35. package/src/config/loader.ts +329 -0
  36. package/src/config/merge.ts +285 -0
  37. package/src/config/types.ts +320 -0
  38. package/src/config/validation.ts +441 -0
  39. package/src/container/forward-ref.ts +143 -0
  40. package/src/container/index.ts +386 -0
  41. package/src/context/index.ts +360 -0
  42. package/src/database/index.ts +1142 -0
  43. package/src/database/migrations/index.ts +371 -0
  44. package/src/database/schema/index.ts +619 -0
  45. package/src/frontend/api-routes.ts +640 -0
  46. package/src/frontend/bundler.ts +643 -0
  47. package/src/frontend/console-client.ts +419 -0
  48. package/src/frontend/console-stream.ts +587 -0
  49. package/src/frontend/dev-server.ts +846 -0
  50. package/src/frontend/file-router.ts +611 -0
  51. package/src/frontend/frameworks/index.ts +106 -0
  52. package/src/frontend/frameworks/react.ts +85 -0
  53. package/src/frontend/frameworks/solid.ts +104 -0
  54. package/src/frontend/frameworks/svelte.ts +110 -0
  55. package/src/frontend/frameworks/vue.ts +92 -0
  56. package/src/frontend/hmr-client.ts +663 -0
  57. package/src/frontend/hmr.ts +728 -0
  58. package/src/frontend/index.ts +342 -0
  59. package/src/frontend/islands.ts +552 -0
  60. package/src/frontend/isr.ts +555 -0
  61. package/src/frontend/layout.ts +475 -0
  62. package/src/frontend/ssr/react.ts +446 -0
  63. package/src/frontend/ssr/solid.ts +523 -0
  64. package/src/frontend/ssr/svelte.ts +546 -0
  65. package/src/frontend/ssr/vue.ts +504 -0
  66. package/src/frontend/ssr.ts +699 -0
  67. package/src/frontend/types.ts +2274 -0
  68. package/src/health/index.ts +604 -0
  69. package/src/index.ts +410 -0
  70. package/src/lock/index.ts +587 -0
  71. package/src/logger/index.ts +444 -0
  72. package/src/logger/transports/index.ts +969 -0
  73. package/src/metrics/index.ts +494 -0
  74. package/src/middleware/built-in.ts +360 -0
  75. package/src/middleware/index.ts +94 -0
  76. package/src/modules/filters.ts +458 -0
  77. package/src/modules/guards.ts +405 -0
  78. package/src/modules/index.ts +1256 -0
  79. package/src/modules/interceptors.ts +574 -0
  80. package/src/modules/lazy.ts +418 -0
  81. package/src/modules/lifecycle.ts +478 -0
  82. package/src/modules/metadata.ts +90 -0
  83. package/src/modules/pipes.ts +626 -0
  84. package/src/router/index.ts +339 -0
  85. package/src/router/linear.ts +371 -0
  86. package/src/router/regex.ts +292 -0
  87. package/src/router/tree.ts +562 -0
  88. package/src/rpc/index.ts +1263 -0
  89. package/src/security/index.ts +436 -0
  90. package/src/ssg/index.ts +631 -0
  91. package/src/storage/index.ts +456 -0
  92. package/src/telemetry/index.ts +1097 -0
  93. package/src/testing/index.ts +1586 -0
  94. package/src/types/index.ts +236 -0
  95. package/src/types/optional-deps.d.ts +219 -0
  96. package/src/validation/index.ts +276 -0
  97. package/src/websocket/index.ts +1004 -0
  98. package/tests/integration/cli.test.ts +1016 -0
  99. package/tests/integration/fullstack.test.ts +234 -0
  100. package/tests/unit/cache.test.ts +174 -0
  101. package/tests/unit/cli-commands.test.ts +892 -0
  102. package/tests/unit/cli.test.ts +1258 -0
  103. package/tests/unit/container.test.ts +279 -0
  104. package/tests/unit/context.test.ts +221 -0
  105. package/tests/unit/database.test.ts +183 -0
  106. package/tests/unit/linear-router.test.ts +280 -0
  107. package/tests/unit/lock.test.ts +336 -0
  108. package/tests/unit/middleware.test.ts +184 -0
  109. package/tests/unit/modules.test.ts +142 -0
  110. package/tests/unit/pubsub.test.ts +257 -0
  111. package/tests/unit/regex-router.test.ts +265 -0
  112. package/tests/unit/router.test.ts +373 -0
  113. package/tests/unit/rpc.test.ts +1248 -0
  114. package/tests/unit/security.test.ts +174 -0
  115. package/tests/unit/telemetry.test.ts +371 -0
  116. package/tests/unit/test-cache.test.ts +110 -0
  117. package/tests/unit/test-database.test.ts +282 -0
  118. package/tests/unit/tree-router.test.ts +325 -0
  119. package/tests/unit/validation.test.ts +794 -0
  120. package/tsconfig.json +27 -0
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Dependency Injection Container
3
+ *
4
+ * Provides inversion of control for managing dependencies
5
+ * with support for constructor injection, factories, and scopes.
6
+ * Supports circular dependency resolution via forward references.
7
+ */
8
+
9
+ import type { Provider } from "../types";
10
+ import {
11
+ type ForwardRef,
12
+ forwardRef,
13
+ isForwardRef,
14
+ resolveForwardRef,
15
+ } from "./forward-ref";
16
+
17
+ export type { Provider } from "../types";
18
+ export { type ForwardRef, forwardRef, isForwardRef, resolveForwardRef };
19
+
20
+ // ============= Token Factory =============
21
+
22
+ /**
23
+ * Token type - represents an injection token
24
+ */
25
+ export type Token<T = unknown> = symbol & { readonly __type?: T };
26
+
27
+ /**
28
+ * Creates a typed injection token (symbol-based)
29
+ */
30
+ export function createToken<T>(description: string): Token<T> {
31
+ return Symbol(description) as Token<T>;
32
+ }
33
+
34
+ /**
35
+ * Token function - creates a typed injection token (symbol-based)
36
+ */
37
+ export const Token = createToken;
38
+
39
+ // ============= Provider Resolution =============
40
+
41
+ interface ResolvedProvider<T = unknown> {
42
+ provider: Provider<T>;
43
+ instance?: T;
44
+ }
45
+
46
+ // ============= Circular Dependency Detection =============
47
+
48
+ class ResolutionStack {
49
+ private stack = new Set<Token>();
50
+ /**
51
+ * Track tokens that are being lazily resolved for circular dependencies.
52
+ * These tokens have a proxy placeholder that will be resolved later.
53
+ */
54
+ private lazyResolutions = new Map<Token, { resolved: boolean; instance?: unknown }>();
55
+
56
+ push(token: Token): void {
57
+ if (this.stack.has(token)) {
58
+ throw new Error(
59
+ "Circular dependency detected: Token already in resolution stack",
60
+ );
61
+ }
62
+ this.stack.add(token);
63
+ }
64
+
65
+ pop(token: Token): void {
66
+ this.stack.delete(token);
67
+ }
68
+
69
+ has(token: Token): boolean {
70
+ return this.stack.has(token);
71
+ }
72
+
73
+ /**
74
+ * Mark a token as being lazily resolved (for circular dependency support)
75
+ */
76
+ markLazy(token: Token, placeholder: { resolved: boolean; instance?: unknown }): void {
77
+ this.lazyResolutions.set(token, placeholder);
78
+ }
79
+
80
+ /**
81
+ * Check if a token has a lazy resolution placeholder
82
+ */
83
+ hasLazy(token: Token): boolean {
84
+ return this.lazyResolutions.has(token);
85
+ }
86
+
87
+ /**
88
+ * Get the lazy resolution placeholder for a token
89
+ */
90
+ getLazy(token: Token): { resolved: boolean; instance?: unknown } | undefined {
91
+ return this.lazyResolutions.get(token);
92
+ }
93
+
94
+ /**
95
+ * Clear lazy resolution for a token after it's fully resolved
96
+ */
97
+ clearLazy(token: Token): void {
98
+ this.lazyResolutions.delete(token);
99
+ }
100
+ }
101
+
102
+ // ============= Container =============
103
+
104
+ export class Container {
105
+ private providers = new Map<Token, ResolvedProvider>();
106
+ private resolutionStack = new ResolutionStack();
107
+
108
+ /**
109
+ * Register a single provider
110
+ */
111
+ register<T>(provider: Provider<T>): void {
112
+ if (this.providers.has(provider.token as Token)) {
113
+ throw new Error(
114
+ `Provider already registered for token: ${String(provider.token)}`,
115
+ );
116
+ }
117
+
118
+ this.providers.set(provider.token as Token, {
119
+ provider: {
120
+ ...provider,
121
+ scope: provider.scope ?? "singleton",
122
+ },
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Register multiple providers at once
128
+ */
129
+ registerAll(providers: Provider[]): void {
130
+ for (const provider of providers) {
131
+ this.register(provider);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Check if a token is registered
137
+ */
138
+ has(token: Token): boolean {
139
+ return this.providers.has(token);
140
+ }
141
+
142
+ /**
143
+ * Resolve a token to its value
144
+ */
145
+ resolve<T>(token: Token<T>): T {
146
+ const resolved = this.providers.get(token as Token);
147
+
148
+ if (!resolved) {
149
+ throw new Error(`No provider registered for token: ${String(token)}`);
150
+ }
151
+
152
+ const { provider } = resolved;
153
+
154
+ // Check for circular dependencies - return a lazy proxy instead of throwing
155
+ if (this.resolutionStack.has(token as Token)) {
156
+ return this.createLazyProxy<T>(token as Token, resolved);
157
+ }
158
+
159
+ // Singleton: return cached instance if available
160
+ if (provider.scope === "singleton" && resolved.instance !== undefined) {
161
+ return resolved.instance as T;
162
+ }
163
+
164
+ // Track resolution for circular dependency detection
165
+ this.resolutionStack.push(token as Token);
166
+
167
+ try {
168
+ const instance = this.createInstance(provider);
169
+
170
+ // Cache singleton instances
171
+ if (provider.scope === "singleton") {
172
+ resolved.instance = instance;
173
+ }
174
+
175
+ return instance as T;
176
+ } finally {
177
+ this.resolutionStack.pop(token as Token);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Create a lazy proxy for circular dependency resolution.
183
+ * The proxy will resolve the actual instance on first property access.
184
+ */
185
+ private createLazyProxy<T>(token: Token, resolved: ResolvedProvider): T {
186
+ // Check if we already have a lazy placeholder for this token
187
+ const existingLazy = this.resolutionStack.getLazy(token);
188
+ if (existingLazy && existingLazy.instance) {
189
+ return existingLazy.instance as T;
190
+ }
191
+
192
+ // Create a placeholder that will be resolved later
193
+ const placeholder: { resolved: boolean; instance?: T } = {
194
+ resolved: false,
195
+ };
196
+ this.resolutionStack.markLazy(token, placeholder);
197
+
198
+ // Create a proxy that lazily resolves the dependency
199
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
200
+ const proxy = new Proxy({} as any, {
201
+ get: (target: T, prop: string | symbol): unknown => {
202
+ // If already resolved, return the cached value
203
+ if (placeholder.resolved && placeholder.instance) {
204
+ const value = (placeholder.instance as Record<string | symbol, unknown>)[prop];
205
+ return typeof value === 'function' ? value.bind(placeholder.instance) : value;
206
+ }
207
+
208
+ // Resolve the actual instance
209
+ // At this point, the circular dependency chain has completed
210
+ // and we can safely get the instance from the resolved provider
211
+ if (resolved.instance !== undefined) {
212
+ placeholder.instance = resolved.instance as T;
213
+ placeholder.resolved = true;
214
+ } else {
215
+ // If not yet cached, we need to wait for the resolution to complete
216
+ // This happens when the proxy is accessed during construction
217
+ // Return a function that will resolve later
218
+ if (prop === 'then') {
219
+ // Make the proxy thenable for async contexts
220
+ return undefined;
221
+ }
222
+ }
223
+
224
+ // Try to get the value from the resolved instance
225
+ if (placeholder.instance) {
226
+ const value = (placeholder.instance as Record<string | symbol, unknown>)[prop];
227
+ return typeof value === 'function' ? value.bind(placeholder.instance) : value;
228
+ }
229
+
230
+ // Return a no-op function for method calls during construction
231
+ return () => undefined;
232
+ },
233
+
234
+ set: (target: T, prop: string | symbol, value: unknown): boolean => {
235
+ if (placeholder.instance) {
236
+ (placeholder.instance as Record<string | symbol, unknown>)[prop] = value;
237
+ return true;
238
+ }
239
+ return false;
240
+ },
241
+
242
+ has: (target: T, prop: string | symbol): boolean => {
243
+ if (placeholder.instance) {
244
+ return prop in (placeholder.instance as object);
245
+ }
246
+ return false;
247
+ },
248
+ });
249
+
250
+ placeholder.instance = proxy;
251
+ return proxy;
252
+ }
253
+
254
+ /**
255
+ * Create an instance from a provider
256
+ */
257
+ private createInstance<T>(provider: Provider<T>): T {
258
+ // useValue: return the value directly
259
+ if (provider.useValue !== undefined) {
260
+ return provider.useValue as T;
261
+ }
262
+
263
+ // useFactory: call factory with injected dependencies
264
+ if (provider.useFactory) {
265
+ const deps = this.resolveDeps(provider.inject ?? []);
266
+ return provider.useFactory(...deps) as T;
267
+ }
268
+
269
+ // useClass: instantiate with injected dependencies
270
+ if (provider.useClass) {
271
+ const deps = this.resolveDeps(provider.inject ?? []);
272
+ // Use new with proper typing for abstract class constructor
273
+ return new (provider.useClass as new (...args: unknown[]) => T)(...deps);
274
+ }
275
+
276
+ throw new Error(
277
+ `Invalid provider configuration for token: ${String(provider.token)}`,
278
+ );
279
+ }
280
+
281
+ /**
282
+ * Resolve an array of dependency tokens (including ForwardRef support)
283
+ */
284
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
285
+ private resolveDeps(tokens: any[]): unknown[] {
286
+ return tokens.map((tokenOrRef) => {
287
+ // Resolve forward reference if needed
288
+ const token = resolveForwardRef(tokenOrRef);
289
+ return this.resolve(token);
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Clear all registrations
295
+ */
296
+ clear(): void {
297
+ this.providers.clear();
298
+ }
299
+
300
+ /**
301
+ * Get all registered tokens
302
+ */
303
+ getTokens(): Token[] {
304
+ return Array.from(this.providers.keys());
305
+ }
306
+
307
+ /**
308
+ * Create a child container (for request-scoped containers)
309
+ */
310
+ createChild(): Container {
311
+ const child = new Container();
312
+ // Copy singleton providers to child
313
+ for (const [token, resolved] of this.providers) {
314
+ if (resolved.provider.scope === "singleton") {
315
+ child.providers.set(token, resolved);
316
+ }
317
+ }
318
+ return child;
319
+ }
320
+ }
321
+
322
+ // ============= Decorator Helpers =============
323
+
324
+ // WeakMap for container metadata storage
325
+ const containerMetadata = new WeakMap<object, Map<string, unknown>>();
326
+
327
+ function setContainerMetadata(
328
+ target: object,
329
+ key: string,
330
+ value: unknown,
331
+ ): void {
332
+ if (!containerMetadata.has(target)) {
333
+ containerMetadata.set(target, new Map());
334
+ }
335
+ containerMetadata.get(target)?.set(key, value);
336
+ }
337
+
338
+ function getContainerMetadata<T>(target: object, key: string): T | undefined {
339
+ return containerMetadata.get(target)?.get(key) as T | undefined;
340
+ }
341
+
342
+ /**
343
+ * Helper to create injectable class decorator
344
+ */
345
+ export function Injectable(token?: Token): ClassDecorator {
346
+ return <TFunction extends Function>(target: TFunction): TFunction => {
347
+ setContainerMetadata(target as object, "injectable", true);
348
+ if (token) {
349
+ setContainerMetadata(target as object, "token", token);
350
+ }
351
+ return target;
352
+ };
353
+ }
354
+
355
+ /**
356
+ * Helper to create parameter injection metadata.
357
+ * Supports both Token and ForwardRef<Token> for circular dependency resolution.
358
+ *
359
+ * @param token - The injection token or a forward reference to the token
360
+ * @returns A parameter decorator that registers the injection metadata
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * // Regular injection
365
+ * constructor(@Inject(MY_TOKEN) private service: MyService) {}
366
+ *
367
+ * // Forward reference for circular dependency
368
+ * constructor(@Inject(forwardRef(() => ServiceB)) private serviceB: ServiceB) {}
369
+ * ```
370
+ */
371
+ export function Inject(token: Token | ForwardRef<Token>): ParameterDecorator {
372
+ return (
373
+ target: unknown,
374
+ propertyKey: string | symbol | undefined,
375
+ parameterIndex: number,
376
+ ) => {
377
+ const targetObj = target as object;
378
+ const existingTokens: Array<Token | ForwardRef<Token>> =
379
+ getContainerMetadata<Array<Token | ForwardRef<Token>>>(targetObj, "inject:tokens") ?? [];
380
+ existingTokens[parameterIndex] = token;
381
+ setContainerMetadata(targetObj, "inject:tokens", existingTokens);
382
+ };
383
+ }
384
+
385
+ // Export getter for use by modules
386
+ export { getContainerMetadata as getInjectTokens };