@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,441 @@
1
+ /**
2
+ * Configuration validation for Bueno Framework
3
+ * Supports Standard Schema validators (Zod, Valibot, ArkType)
4
+ */
5
+
6
+ import type { StandardSchema, StandardIssue } from "../types";
7
+ import type { BuenoConfig, DeepPartial } from "./types";
8
+
9
+ /**
10
+ * Validation result
11
+ */
12
+ export interface ConfigValidationResult {
13
+ /** Whether validation passed */
14
+ valid: boolean;
15
+ /** Validation errors if any */
16
+ errors: ConfigValidationError[];
17
+ /** Warnings (non-critical issues) */
18
+ warnings: ConfigValidationWarning[];
19
+ }
20
+
21
+ /**
22
+ * Validation error
23
+ */
24
+ export interface ConfigValidationError {
25
+ /** Error message */
26
+ message: string;
27
+ /** Path to the invalid field */
28
+ path?: string;
29
+ /** Expected type or value */
30
+ expected?: string;
31
+ /** Actual value received */
32
+ received?: unknown;
33
+ /** Original issue from schema validator */
34
+ issue?: StandardIssue;
35
+ }
36
+
37
+ /**
38
+ * Validation warning
39
+ */
40
+ export interface ConfigValidationWarning {
41
+ /** Warning message */
42
+ message: string;
43
+ /** Path to the field */
44
+ path?: string;
45
+ }
46
+
47
+ /**
48
+ * Check if a value is a Standard Schema
49
+ */
50
+ export function isStandardSchema(value: unknown): value is StandardSchema {
51
+ return (
52
+ typeof value === "object" &&
53
+ value !== null &&
54
+ "~standard" in value &&
55
+ typeof (value as StandardSchema)["~standard"] === "object" &&
56
+ typeof (value as StandardSchema)["~standard"].validate === "function"
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Validate config against a Standard Schema
62
+ */
63
+ export async function validateWithSchema<T>(
64
+ config: unknown,
65
+ schema: StandardSchema<T>,
66
+ ): Promise<ConfigValidationResult> {
67
+ const result = await schema["~standard"].validate(config);
68
+
69
+ if (result.issues === undefined) {
70
+ return {
71
+ valid: true,
72
+ errors: [],
73
+ warnings: [],
74
+ };
75
+ }
76
+
77
+ const errors: ConfigValidationError[] = result.issues.map((issue) => ({
78
+ message: issue.message,
79
+ path: formatPath(issue.path),
80
+ issue,
81
+ }));
82
+
83
+ return {
84
+ valid: false,
85
+ errors,
86
+ warnings: [],
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Format a path from Standard Schema issues
92
+ */
93
+ function formatPath(
94
+ path?: ReadonlyArray<PropertyKey | { key: PropertyKey }>,
95
+ ): string | undefined {
96
+ if (!path || path.length === 0) {
97
+ return undefined;
98
+ }
99
+
100
+ return path
101
+ .map((segment) => {
102
+ if (typeof segment === "object" && "key" in segment) {
103
+ return String(segment.key);
104
+ }
105
+ return String(segment);
106
+ })
107
+ .join(".");
108
+ }
109
+
110
+ /**
111
+ * Default validation rules for BuenoConfig
112
+ */
113
+ const DEFAULT_RULES: ValidationRule[] = [
114
+ {
115
+ path: "server.port",
116
+ validate: (value) => {
117
+ if (value === undefined) return { valid: true };
118
+ if (typeof value !== "number") {
119
+ return { valid: false, message: "Port must be a number" };
120
+ }
121
+ if (value < 0 || value > 65535) {
122
+ return { valid: false, message: "Port must be between 0 and 65535" };
123
+ }
124
+ return { valid: true };
125
+ },
126
+ },
127
+ {
128
+ path: "database.poolSize",
129
+ validate: (value) => {
130
+ if (value === undefined) return { valid: true };
131
+ if (typeof value !== "number") {
132
+ return { valid: false, message: "Pool size must be a number" };
133
+ }
134
+ if (value < 1) {
135
+ return { valid: false, message: "Pool size must be at least 1" };
136
+ }
137
+ return { valid: true };
138
+ },
139
+ },
140
+ {
141
+ path: "database.slowQueryThreshold",
142
+ validate: (value) => {
143
+ if (value === undefined) return { valid: true };
144
+ if (typeof value !== "number") {
145
+ return { valid: false, message: "Slow query threshold must be a number" };
146
+ }
147
+ if (value < 0) {
148
+ return { valid: false, message: "Slow query threshold must be non-negative" };
149
+ }
150
+ return { valid: true };
151
+ },
152
+ },
153
+ {
154
+ path: "cache.ttl",
155
+ validate: (value) => {
156
+ if (value === undefined) return { valid: true };
157
+ if (typeof value !== "number") {
158
+ return { valid: false, message: "TTL must be a number" };
159
+ }
160
+ if (value < 0) {
161
+ return { valid: false, message: "TTL must be non-negative" };
162
+ }
163
+ return { valid: true };
164
+ },
165
+ },
166
+ {
167
+ path: "cache.driver",
168
+ validate: (value) => {
169
+ if (value === undefined) return { valid: true };
170
+ if (value !== "redis" && value !== "memory") {
171
+ return {
172
+ valid: false,
173
+ message: 'Cache driver must be "redis" or "memory"',
174
+ expected: '"redis" | "memory"',
175
+ received: value,
176
+ };
177
+ }
178
+ return { valid: true };
179
+ },
180
+ },
181
+ {
182
+ path: "logger.level",
183
+ validate: (value) => {
184
+ if (value === undefined) return { valid: true };
185
+ const validLevels = ["debug", "info", "warn", "error", "fatal"];
186
+ if (!validLevels.includes(value as string)) {
187
+ return {
188
+ valid: false,
189
+ message: `Logger level must be one of: ${validLevels.join(", ")}`,
190
+ expected: validLevels.join(" | "),
191
+ received: value,
192
+ };
193
+ }
194
+ return { valid: true };
195
+ },
196
+ },
197
+ {
198
+ path: "telemetry.sampleRate",
199
+ validate: (value) => {
200
+ if (value === undefined) return { valid: true };
201
+ if (typeof value !== "number") {
202
+ return { valid: false, message: "Sample rate must be a number" };
203
+ }
204
+ if (value < 0 || value > 1) {
205
+ return { valid: false, message: "Sample rate must be between 0 and 1" };
206
+ }
207
+ return { valid: true };
208
+ },
209
+ },
210
+ {
211
+ path: "metrics.collectInterval",
212
+ validate: (value) => {
213
+ if (value === undefined) return { valid: true };
214
+ if (typeof value !== "number") {
215
+ return { valid: false, message: "Collect interval must be a number" };
216
+ }
217
+ if (value < 0) {
218
+ return { valid: false, message: "Collect interval must be non-negative" };
219
+ }
220
+ return { valid: true };
221
+ },
222
+ },
223
+ ];
224
+
225
+ /**
226
+ * Validation rule
227
+ */
228
+ interface ValidationRule {
229
+ /** Path to the field to validate */
230
+ path: string;
231
+ /** Validation function */
232
+ validate: (
233
+ value: unknown,
234
+ config: DeepPartial<BuenoConfig>,
235
+ ) => ValidationResult;
236
+ }
237
+
238
+ /**
239
+ * Validation result from a rule
240
+ */
241
+ interface ValidationResult {
242
+ valid: boolean;
243
+ message?: string;
244
+ expected?: string;
245
+ received?: unknown;
246
+ }
247
+
248
+ /**
249
+ * Get a value from an object using dot notation path
250
+ */
251
+ function getValueByPath(obj: unknown, path: string): unknown {
252
+ const parts = path.split(".");
253
+ let current: unknown = obj;
254
+
255
+ for (const part of parts) {
256
+ if (current === null || current === undefined) {
257
+ return undefined;
258
+ }
259
+ if (typeof current !== "object") {
260
+ return undefined;
261
+ }
262
+ current = (current as Record<string, unknown>)[part];
263
+ }
264
+
265
+ return current;
266
+ }
267
+
268
+ /**
269
+ * Validate config using default rules
270
+ */
271
+ export function validateConfigDefaults(
272
+ config: DeepPartial<BuenoConfig>,
273
+ ): ConfigValidationResult {
274
+ const errors: ConfigValidationError[] = [];
275
+ const warnings: ConfigValidationWarning[] = [];
276
+
277
+ for (const rule of DEFAULT_RULES) {
278
+ const value = getValueByPath(config, rule.path);
279
+ const result = rule.validate(value, config);
280
+
281
+ if (!result.valid) {
282
+ errors.push({
283
+ message: result.message || "Validation failed",
284
+ path: rule.path,
285
+ expected: result.expected,
286
+ received: result.received,
287
+ });
288
+ }
289
+ }
290
+
291
+ // Add warnings for potentially missing critical config
292
+ if (!config.database?.url && !process.env.DATABASE_URL) {
293
+ warnings.push({
294
+ message: "No database URL configured",
295
+ path: "database.url",
296
+ });
297
+ }
298
+
299
+ if (config.cache?.driver === "redis" && !config.cache.url && !process.env.REDIS_URL) {
300
+ warnings.push({
301
+ message: "Redis cache driver selected but no Redis URL configured",
302
+ path: "cache.url",
303
+ });
304
+ }
305
+
306
+ return {
307
+ valid: errors.length === 0,
308
+ errors,
309
+ warnings,
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Validate configuration
315
+ * Supports both Standard Schema and default validation rules
316
+ */
317
+ export async function validateConfig<T extends BuenoConfig = BuenoConfig>(
318
+ config: unknown,
319
+ schema?: StandardSchema<T>,
320
+ ): Promise<ConfigValidationResult> {
321
+ // If a schema is provided, use it
322
+ if (schema && isStandardSchema(schema)) {
323
+ return validateWithSchema(config, schema);
324
+ }
325
+
326
+ // Otherwise, use default validation
327
+ return validateConfigDefaults(config as DeepPartial<BuenoConfig>);
328
+ }
329
+
330
+ /**
331
+ * Validate configuration synchronously
332
+ * Note: Standard Schema validation may be async, so this only works with default rules
333
+ */
334
+ export function validateConfigSync(
335
+ config: DeepPartial<BuenoConfig>,
336
+ ): ConfigValidationResult {
337
+ return validateConfigDefaults(config);
338
+ }
339
+
340
+ /**
341
+ * Create a validation error with helpful message
342
+ */
343
+ export function createConfigError(
344
+ result: ConfigValidationResult,
345
+ ): ConfigValidationError {
346
+ if (result.errors.length === 0) {
347
+ return {
348
+ message: "Unknown validation error",
349
+ };
350
+ }
351
+
352
+ // Return the first error with context
353
+ const firstError = result.errors[0];
354
+ return {
355
+ message: firstError.message,
356
+ path: firstError.path,
357
+ expected: firstError.expected,
358
+ received: firstError.received,
359
+ };
360
+ }
361
+
362
+ /**
363
+ * Format validation errors for display
364
+ */
365
+ export function formatValidationErrors(
366
+ errors: ConfigValidationError[],
367
+ ): string {
368
+ if (errors.length === 0) {
369
+ return "No errors";
370
+ }
371
+
372
+ const lines = errors.map((error, index) => {
373
+ let line = `${index + 1}. ${error.message}`;
374
+ if (error.path) {
375
+ line += ` (at ${error.path})`;
376
+ }
377
+ if (error.expected) {
378
+ line += `\n Expected: ${error.expected}`;
379
+ }
380
+ if (error.received !== undefined) {
381
+ line += `\n Received: ${JSON.stringify(error.received)}`;
382
+ }
383
+ return line;
384
+ });
385
+
386
+ return `Configuration validation failed:\n${lines.join("\n")}`;
387
+ }
388
+
389
+ /**
390
+ * Assert that configuration is valid
391
+ * Throws an error if validation fails
392
+ */
393
+ export async function assertValidConfig<T extends BuenoConfig = BuenoConfig>(
394
+ config: unknown,
395
+ schema?: StandardSchema<T>,
396
+ ): Promise<void> {
397
+ const result = await validateConfig(config, schema);
398
+
399
+ if (!result.valid) {
400
+ const errorMessage = formatValidationErrors(result.errors);
401
+ throw new Error(errorMessage);
402
+ }
403
+
404
+ // Log warnings
405
+ if (result.warnings.length > 0) {
406
+ for (const warning of result.warnings) {
407
+ console.warn(`Config warning: ${warning.message}${warning.path ? ` (at ${warning.path})` : ""}`);
408
+ }
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Add custom validation rules
414
+ */
415
+ export function createCustomValidator(
416
+ rules: ValidationRule[],
417
+ ): (config: DeepPartial<BuenoConfig>) => ConfigValidationResult {
418
+ return (config) => {
419
+ const errors: ConfigValidationError[] = [];
420
+
421
+ for (const rule of rules) {
422
+ const value = getValueByPath(config, rule.path);
423
+ const result = rule.validate(value, config);
424
+
425
+ if (!result.valid) {
426
+ errors.push({
427
+ message: result.message || "Validation failed",
428
+ path: rule.path,
429
+ expected: result.expected,
430
+ received: result.received,
431
+ });
432
+ }
433
+ }
434
+
435
+ return {
436
+ valid: errors.length === 0,
437
+ errors,
438
+ warnings: [],
439
+ };
440
+ };
441
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Forward Reference for Circular Dependencies
3
+ *
4
+ * Provides a way to resolve circular dependencies by deferring the resolution
5
+ * of a dependency until it's actually needed. This allows two or more services
6
+ * to depend on each other without causing infinite loops during instantiation.
7
+ */
8
+
9
+ /**
10
+ * Forward reference container for lazy resolution of circular dependencies.
11
+ *
12
+ * @template T - The type of the referenced value
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Creating a forward reference
17
+ * const ref = forwardRef(() => ServiceB);
18
+ *
19
+ * // Using with @Inject decorator
20
+ * @Injectable()
21
+ * class ServiceA {
22
+ * constructor(
23
+ * @Inject(forwardRef(() => ServiceB))
24
+ * private serviceB: ServiceB
25
+ * ) {}
26
+ * }
27
+ * ```
28
+ */
29
+ export interface ForwardRef<T> {
30
+ /**
31
+ * The unique symbol identifying this as a ForwardRef
32
+ */
33
+ readonly __forwardRef: unique symbol;
34
+
35
+ /**
36
+ * Factory function that returns the actual value when called.
37
+ * This is invoked lazily when the dependency is first accessed.
38
+ */
39
+ forwardRef: () => T;
40
+ }
41
+
42
+ /**
43
+ * Symbol used to identify ForwardRef objects
44
+ */
45
+ const FORWARD_REF_SYMBOL = Symbol.for('buno.forwardRef');
46
+
47
+ /**
48
+ * Create a forward reference for circular dependency resolution.
49
+ *
50
+ * The provided factory function is called lazily when the dependency
51
+ * is actually resolved, allowing the referenced class to be defined
52
+ * later in the module loading process.
53
+ *
54
+ * @template T - The type of the referenced value
55
+ * @param fn - Factory function that returns the actual token or value
56
+ * @returns A ForwardRef object that can be used with @Inject()
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // service-a.ts
61
+ * @Injectable()
62
+ * export class ServiceA {
63
+ * constructor(
64
+ * @Inject(forwardRef(() => ServiceB))
65
+ * private serviceB: ServiceB
66
+ * ) {}
67
+ *
68
+ * doSomething() {
69
+ * return this.serviceB.help();
70
+ * }
71
+ * }
72
+ *
73
+ * // service-b.ts
74
+ * @Injectable()
75
+ * export class ServiceB {
76
+ * constructor(
77
+ * @Inject(forwardRef(() => ServiceA))
78
+ * private serviceA: ServiceA
79
+ * ) {}
80
+ *
81
+ * help() {
82
+ * return 'helping';
83
+ * }
84
+ * }
85
+ * ```
86
+ */
87
+ export function forwardRef<T>(fn: () => T): ForwardRef<T> {
88
+ return {
89
+ __forwardRef: FORWARD_REF_SYMBOL,
90
+ forwardRef: fn,
91
+ } as unknown as ForwardRef<T>;
92
+ }
93
+
94
+ /**
95
+ * Type guard to check if a value is a ForwardRef.
96
+ *
97
+ * @param value - The value to check
98
+ * @returns True if the value is a ForwardRef, false otherwise
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const ref = forwardRef(() => MyService);
103
+ * if (isForwardRef(ref)) {
104
+ * const actualToken = resolveForwardRef(ref);
105
+ * }
106
+ * ```
107
+ */
108
+ export function isForwardRef(value: unknown): value is ForwardRef<unknown> {
109
+ return (
110
+ typeof value === 'object' &&
111
+ value !== null &&
112
+ '__forwardRef' in value &&
113
+ 'forwardRef' in value &&
114
+ typeof (value as ForwardRef<unknown>).forwardRef === 'function'
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Resolve a forward reference to its actual value.
120
+ *
121
+ * If the provided value is a ForwardRef, this function calls its
122
+ * factory function to get the actual value. If it's not a ForwardRef,
123
+ * the value is returned as-is.
124
+ *
125
+ * @template T - The expected type of the resolved value
126
+ * @param ref - Either a ForwardRef or a direct value
127
+ * @returns The resolved value
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const token = Token<ServiceB>('ServiceB');
132
+ * const ref = forwardRef(() => token);
133
+ *
134
+ * // Resolves to the token
135
+ * const actualToken = resolveForwardRef(ref);
136
+ * ```
137
+ */
138
+ export function resolveForwardRef<T>(ref: ForwardRef<T> | T): T {
139
+ if (isForwardRef(ref)) {
140
+ return ref.forwardRef();
141
+ }
142
+ return ref;
143
+ }