@buenojs/bueno 0.8.4 → 0.8.5
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/README.md +136 -16
- package/dist/cli/{index.js → bin.js} +412 -331
- package/dist/container/index.js +250 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +11043 -6482
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3346 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +776 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/package.json +121 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +294 -232
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +37 -18
- package/src/cli/templates/database/mysql.ts +3 -3
- package/src/cli/templates/database/none.ts +2 -2
- package/src/cli/templates/database/postgresql.ts +3 -3
- package/src/cli/templates/database/sqlite.ts +3 -3
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +33 -15
- package/src/cli/templates/frontend/none.ts +2 -2
- package/src/cli/templates/frontend/react.ts +18 -18
- package/src/cli/templates/frontend/solid.ts +15 -15
- package/src/cli/templates/frontend/svelte.ts +17 -17
- package/src/cli/templates/frontend/vue.ts +15 -15
- package/src/cli/templates/generators/index.ts +29 -29
- package/src/cli/templates/generators/types.ts +21 -21
- package/src/cli/templates/index.ts +6 -6
- package/src/cli/templates/project/api.ts +37 -36
- package/src/cli/templates/project/default.ts +25 -25
- package/src/cli/templates/project/fullstack.ts +28 -26
- package/src/cli/templates/project/index.ts +55 -16
- package/src/cli/templates/project/minimal.ts +17 -12
- package/src/cli/templates/project/types.ts +10 -5
- package/src/cli/templates/project/website.ts +14 -14
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -3
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +14 -8
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +545 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +179 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +409 -298
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
package/src/config/validation.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Supports Standard Schema validators (Zod, Valibot, ArkType)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { StandardIssue, StandardSchema } from "../types";
|
|
7
7
|
import type { BuenoConfig, DeepPartial } from "./types";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -142,10 +142,16 @@ const DEFAULT_RULES: ValidationRule[] = [
|
|
|
142
142
|
validate: (value) => {
|
|
143
143
|
if (value === undefined) return { valid: true };
|
|
144
144
|
if (typeof value !== "number") {
|
|
145
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
message: "Slow query threshold must be a number",
|
|
148
|
+
};
|
|
146
149
|
}
|
|
147
150
|
if (value < 0) {
|
|
148
|
-
return {
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
message: "Slow query threshold must be non-negative",
|
|
154
|
+
};
|
|
149
155
|
}
|
|
150
156
|
return { valid: true };
|
|
151
157
|
},
|
|
@@ -215,7 +221,203 @@ const DEFAULT_RULES: ValidationRule[] = [
|
|
|
215
221
|
return { valid: false, message: "Collect interval must be a number" };
|
|
216
222
|
}
|
|
217
223
|
if (value < 0) {
|
|
218
|
-
return {
|
|
224
|
+
return {
|
|
225
|
+
valid: false,
|
|
226
|
+
message: "Collect interval must be non-negative",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return { valid: true };
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
path: "jobs.driver",
|
|
234
|
+
validate: (value) => {
|
|
235
|
+
if (value === undefined) return { valid: true };
|
|
236
|
+
if (value !== "redis" && value !== "memory") {
|
|
237
|
+
return {
|
|
238
|
+
valid: false,
|
|
239
|
+
message: 'Jobs driver must be "redis" or "memory"',
|
|
240
|
+
expected: '"redis" | "memory"',
|
|
241
|
+
received: value,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return { valid: true };
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
path: "jobs.concurrency",
|
|
249
|
+
validate: (value) => {
|
|
250
|
+
if (value === undefined) return { valid: true };
|
|
251
|
+
if (typeof value !== "number") {
|
|
252
|
+
return { valid: false, message: "Concurrency must be a number" };
|
|
253
|
+
}
|
|
254
|
+
if (value < 1) {
|
|
255
|
+
return { valid: false, message: "Concurrency must be at least 1" };
|
|
256
|
+
}
|
|
257
|
+
return { valid: true };
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
path: "jobs.maxRetries",
|
|
262
|
+
validate: (value) => {
|
|
263
|
+
if (value === undefined) return { valid: true };
|
|
264
|
+
if (typeof value !== "number") {
|
|
265
|
+
return { valid: false, message: "Max retries must be a number" };
|
|
266
|
+
}
|
|
267
|
+
if (value < 0) {
|
|
268
|
+
return { valid: false, message: "Max retries must be non-negative" };
|
|
269
|
+
}
|
|
270
|
+
return { valid: true };
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
path: "jobs.retryDelay",
|
|
275
|
+
validate: (value) => {
|
|
276
|
+
if (value === undefined) return { valid: true };
|
|
277
|
+
if (typeof value !== "number") {
|
|
278
|
+
return { valid: false, message: "Retry delay must be a number" };
|
|
279
|
+
}
|
|
280
|
+
if (value < 0) {
|
|
281
|
+
return { valid: false, message: "Retry delay must be non-negative" };
|
|
282
|
+
}
|
|
283
|
+
return { valid: true };
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
path: "jobs.batchSize",
|
|
288
|
+
validate: (value) => {
|
|
289
|
+
if (value === undefined) return { valid: true };
|
|
290
|
+
if (typeof value !== "number") {
|
|
291
|
+
return { valid: false, message: "Batch size must be a number" };
|
|
292
|
+
}
|
|
293
|
+
if (value < 1) {
|
|
294
|
+
return { valid: false, message: "Batch size must be at least 1" };
|
|
295
|
+
}
|
|
296
|
+
return { valid: true };
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
path: "jobs.pollInterval",
|
|
301
|
+
validate: (value) => {
|
|
302
|
+
if (value === undefined) return { valid: true };
|
|
303
|
+
if (typeof value !== "number") {
|
|
304
|
+
return { valid: false, message: "Poll interval must be a number" };
|
|
305
|
+
}
|
|
306
|
+
if (value < 100) {
|
|
307
|
+
return {
|
|
308
|
+
valid: false,
|
|
309
|
+
message: "Poll interval must be at least 100ms",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return { valid: true };
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
path: "jobs.jobTimeout",
|
|
317
|
+
validate: (value) => {
|
|
318
|
+
if (value === undefined) return { valid: true };
|
|
319
|
+
if (typeof value !== "number") {
|
|
320
|
+
return { valid: false, message: "Job timeout must be a number" };
|
|
321
|
+
}
|
|
322
|
+
if (value < 1000) {
|
|
323
|
+
return { valid: false, message: "Job timeout must be at least 1000ms" };
|
|
324
|
+
}
|
|
325
|
+
return { valid: true };
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
path: "notification.email.driver",
|
|
330
|
+
validate: (value) => {
|
|
331
|
+
if (value === undefined) return { valid: true };
|
|
332
|
+
const validDrivers = ["smtp", "sendgrid", "brevo", "resend"];
|
|
333
|
+
if (!validDrivers.includes(value as string)) {
|
|
334
|
+
return {
|
|
335
|
+
valid: false,
|
|
336
|
+
message: `Email driver must be one of: ${validDrivers.join(", ")}`,
|
|
337
|
+
expected: validDrivers.join(" | "),
|
|
338
|
+
received: value,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return { valid: true };
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
path: "notification.email.from",
|
|
346
|
+
validate: (value) => {
|
|
347
|
+
if (value === undefined) return { valid: true };
|
|
348
|
+
if (typeof value !== "string") {
|
|
349
|
+
return { valid: false, message: "From email must be a string" };
|
|
350
|
+
}
|
|
351
|
+
if (!value.includes("@")) {
|
|
352
|
+
return {
|
|
353
|
+
valid: false,
|
|
354
|
+
message: "From email must be a valid email address",
|
|
355
|
+
received: value,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return { valid: true };
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
path: "notification.email.smtp.port",
|
|
363
|
+
validate: (value) => {
|
|
364
|
+
if (value === undefined) return { valid: true };
|
|
365
|
+
if (typeof value !== "number") {
|
|
366
|
+
return { valid: false, message: "SMTP port must be a number" };
|
|
367
|
+
}
|
|
368
|
+
if (value < 1 || value > 65535) {
|
|
369
|
+
return {
|
|
370
|
+
valid: false,
|
|
371
|
+
message: "SMTP port must be between 1 and 65535",
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return { valid: true };
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
path: "notification.sms.driver",
|
|
379
|
+
validate: (value) => {
|
|
380
|
+
if (value === undefined) return { valid: true };
|
|
381
|
+
const validDrivers = ["twilio", "aws-sns", "custom"];
|
|
382
|
+
if (!validDrivers.includes(value as string)) {
|
|
383
|
+
return {
|
|
384
|
+
valid: false,
|
|
385
|
+
message: `SMS driver must be one of: ${validDrivers.join(", ")}`,
|
|
386
|
+
expected: validDrivers.join(" | "),
|
|
387
|
+
received: value,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
return { valid: true };
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
path: "notification.whatsapp.driver",
|
|
395
|
+
validate: (value) => {
|
|
396
|
+
if (value === undefined) return { valid: true };
|
|
397
|
+
const validDrivers = ["twilio", "custom"];
|
|
398
|
+
if (!validDrivers.includes(value as string)) {
|
|
399
|
+
return {
|
|
400
|
+
valid: false,
|
|
401
|
+
message: `WhatsApp driver must be one of: ${validDrivers.join(", ")}`,
|
|
402
|
+
expected: validDrivers.join(" | "),
|
|
403
|
+
received: value,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
return { valid: true };
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
path: "notification.push.driver",
|
|
411
|
+
validate: (value) => {
|
|
412
|
+
if (value === undefined) return { valid: true };
|
|
413
|
+
const validDrivers = ["firebase", "apns", "custom"];
|
|
414
|
+
if (!validDrivers.includes(value as string)) {
|
|
415
|
+
return {
|
|
416
|
+
valid: false,
|
|
417
|
+
message: `Push driver must be one of: ${validDrivers.join(", ")}`,
|
|
418
|
+
expected: validDrivers.join(" | "),
|
|
419
|
+
received: value,
|
|
420
|
+
};
|
|
219
421
|
}
|
|
220
422
|
return { valid: true };
|
|
221
423
|
},
|
|
@@ -296,7 +498,11 @@ export function validateConfigDefaults(
|
|
|
296
498
|
});
|
|
297
499
|
}
|
|
298
500
|
|
|
299
|
-
if (
|
|
501
|
+
if (
|
|
502
|
+
config.cache?.driver === "redis" &&
|
|
503
|
+
!config.cache.url &&
|
|
504
|
+
!process.env.REDIS_URL
|
|
505
|
+
) {
|
|
300
506
|
warnings.push({
|
|
301
507
|
message: "Redis cache driver selected but no Redis URL configured",
|
|
302
508
|
path: "cache.url",
|
|
@@ -404,7 +610,9 @@ export async function assertValidConfig<T extends BuenoConfig = BuenoConfig>(
|
|
|
404
610
|
// Log warnings
|
|
405
611
|
if (result.warnings.length > 0) {
|
|
406
612
|
for (const warning of result.warnings) {
|
|
407
|
-
console.warn(
|
|
613
|
+
console.warn(
|
|
614
|
+
`Config warning: ${warning.message}${warning.path ? ` (at ${warning.path})` : ""}`,
|
|
615
|
+
);
|
|
408
616
|
}
|
|
409
617
|
}
|
|
410
618
|
}
|
|
@@ -438,4 +646,4 @@ export function createCustomValidator(
|
|
|
438
646
|
warnings: [],
|
|
439
647
|
};
|
|
440
648
|
};
|
|
441
|
-
}
|
|
649
|
+
}
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Forward reference container for lazy resolution of circular dependencies.
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* @template T - The type of the referenced value
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```typescript
|
|
16
16
|
* // Creating a forward reference
|
|
17
17
|
* const ref = forwardRef(() => ServiceB);
|
|
18
|
-
*
|
|
18
|
+
*
|
|
19
19
|
* // Using with @Inject decorator
|
|
20
20
|
* @Injectable()
|
|
21
21
|
* class ServiceA {
|
|
@@ -31,7 +31,7 @@ export interface ForwardRef<T> {
|
|
|
31
31
|
* The unique symbol identifying this as a ForwardRef
|
|
32
32
|
*/
|
|
33
33
|
readonly __forwardRef: unique symbol;
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
/**
|
|
36
36
|
* Factory function that returns the actual value when called.
|
|
37
37
|
* This is invoked lazily when the dependency is first accessed.
|
|
@@ -42,19 +42,19 @@ export interface ForwardRef<T> {
|
|
|
42
42
|
/**
|
|
43
43
|
* Symbol used to identify ForwardRef objects
|
|
44
44
|
*/
|
|
45
|
-
const FORWARD_REF_SYMBOL = Symbol.for(
|
|
45
|
+
const FORWARD_REF_SYMBOL = Symbol.for("buno.forwardRef");
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Create a forward reference for circular dependency resolution.
|
|
49
|
-
*
|
|
49
|
+
*
|
|
50
50
|
* The provided factory function is called lazily when the dependency
|
|
51
51
|
* is actually resolved, allowing the referenced class to be defined
|
|
52
52
|
* later in the module loading process.
|
|
53
|
-
*
|
|
53
|
+
*
|
|
54
54
|
* @template T - The type of the referenced value
|
|
55
55
|
* @param fn - Factory function that returns the actual token or value
|
|
56
56
|
* @returns A ForwardRef object that can be used with @Inject()
|
|
57
|
-
*
|
|
57
|
+
*
|
|
58
58
|
* @example
|
|
59
59
|
* ```typescript
|
|
60
60
|
* // service-a.ts
|
|
@@ -64,12 +64,12 @@ const FORWARD_REF_SYMBOL = Symbol.for('buno.forwardRef');
|
|
|
64
64
|
* @Inject(forwardRef(() => ServiceB))
|
|
65
65
|
* private serviceB: ServiceB
|
|
66
66
|
* ) {}
|
|
67
|
-
*
|
|
67
|
+
*
|
|
68
68
|
* doSomething() {
|
|
69
69
|
* return this.serviceB.help();
|
|
70
70
|
* }
|
|
71
71
|
* }
|
|
72
|
-
*
|
|
72
|
+
*
|
|
73
73
|
* // service-b.ts
|
|
74
74
|
* @Injectable()
|
|
75
75
|
* export class ServiceB {
|
|
@@ -77,7 +77,7 @@ const FORWARD_REF_SYMBOL = Symbol.for('buno.forwardRef');
|
|
|
77
77
|
* @Inject(forwardRef(() => ServiceA))
|
|
78
78
|
* private serviceA: ServiceA
|
|
79
79
|
* ) {}
|
|
80
|
-
*
|
|
80
|
+
*
|
|
81
81
|
* help() {
|
|
82
82
|
* return 'helping';
|
|
83
83
|
* }
|
|
@@ -93,10 +93,10 @@ export function forwardRef<T>(fn: () => T): ForwardRef<T> {
|
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* Type guard to check if a value is a ForwardRef.
|
|
96
|
-
*
|
|
96
|
+
*
|
|
97
97
|
* @param value - The value to check
|
|
98
98
|
* @returns True if the value is a ForwardRef, false otherwise
|
|
99
|
-
*
|
|
99
|
+
*
|
|
100
100
|
* @example
|
|
101
101
|
* ```typescript
|
|
102
102
|
* const ref = forwardRef(() => MyService);
|
|
@@ -107,30 +107,30 @@ export function forwardRef<T>(fn: () => T): ForwardRef<T> {
|
|
|
107
107
|
*/
|
|
108
108
|
export function isForwardRef(value: unknown): value is ForwardRef<unknown> {
|
|
109
109
|
return (
|
|
110
|
-
typeof value ===
|
|
110
|
+
typeof value === "object" &&
|
|
111
111
|
value !== null &&
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
typeof (value as ForwardRef<unknown>).forwardRef ===
|
|
112
|
+
"__forwardRef" in value &&
|
|
113
|
+
"forwardRef" in value &&
|
|
114
|
+
typeof (value as ForwardRef<unknown>).forwardRef === "function"
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Resolve a forward reference to its actual value.
|
|
120
|
-
*
|
|
120
|
+
*
|
|
121
121
|
* If the provided value is a ForwardRef, this function calls its
|
|
122
122
|
* factory function to get the actual value. If it's not a ForwardRef,
|
|
123
123
|
* the value is returned as-is.
|
|
124
|
-
*
|
|
124
|
+
*
|
|
125
125
|
* @template T - The expected type of the resolved value
|
|
126
126
|
* @param ref - Either a ForwardRef or a direct value
|
|
127
127
|
* @returns The resolved value
|
|
128
|
-
*
|
|
128
|
+
*
|
|
129
129
|
* @example
|
|
130
130
|
* ```typescript
|
|
131
131
|
* const token = Token<ServiceB>('ServiceB');
|
|
132
132
|
* const ref = forwardRef(() => token);
|
|
133
|
-
*
|
|
133
|
+
*
|
|
134
134
|
* // Resolves to the token
|
|
135
135
|
* const actualToken = resolveForwardRef(ref);
|
|
136
136
|
* ```
|
|
@@ -140,4 +140,4 @@ export function resolveForwardRef<T>(ref: ForwardRef<T> | T): T {
|
|
|
140
140
|
return ref.forwardRef();
|
|
141
141
|
}
|
|
142
142
|
return ref;
|
|
143
|
-
}
|
|
143
|
+
}
|
package/src/container/index.ts
CHANGED
|
@@ -51,7 +51,10 @@ class ResolutionStack {
|
|
|
51
51
|
* Track tokens that are being lazily resolved for circular dependencies.
|
|
52
52
|
* These tokens have a proxy placeholder that will be resolved later.
|
|
53
53
|
*/
|
|
54
|
-
private lazyResolutions = new Map<
|
|
54
|
+
private lazyResolutions = new Map<
|
|
55
|
+
Token,
|
|
56
|
+
{ resolved: boolean; instance?: unknown }
|
|
57
|
+
>();
|
|
55
58
|
|
|
56
59
|
push(token: Token): void {
|
|
57
60
|
if (this.stack.has(token)) {
|
|
@@ -73,7 +76,10 @@ class ResolutionStack {
|
|
|
73
76
|
/**
|
|
74
77
|
* Mark a token as being lazily resolved (for circular dependency support)
|
|
75
78
|
*/
|
|
76
|
-
markLazy(
|
|
79
|
+
markLazy(
|
|
80
|
+
token: Token,
|
|
81
|
+
placeholder: { resolved: boolean; instance?: unknown },
|
|
82
|
+
): void {
|
|
77
83
|
this.lazyResolutions.set(token, placeholder);
|
|
78
84
|
}
|
|
79
85
|
|
|
@@ -201,8 +207,12 @@ export class Container {
|
|
|
201
207
|
get: (target: T, prop: string | symbol): unknown => {
|
|
202
208
|
// If already resolved, return the cached value
|
|
203
209
|
if (placeholder.resolved && placeholder.instance) {
|
|
204
|
-
const value = (
|
|
205
|
-
|
|
210
|
+
const value = (
|
|
211
|
+
placeholder.instance as Record<string | symbol, unknown>
|
|
212
|
+
)[prop];
|
|
213
|
+
return typeof value === "function"
|
|
214
|
+
? value.bind(placeholder.instance)
|
|
215
|
+
: value;
|
|
206
216
|
}
|
|
207
217
|
|
|
208
218
|
// Resolve the actual instance
|
|
@@ -215,7 +225,7 @@ export class Container {
|
|
|
215
225
|
// If not yet cached, we need to wait for the resolution to complete
|
|
216
226
|
// This happens when the proxy is accessed during construction
|
|
217
227
|
// Return a function that will resolve later
|
|
218
|
-
if (prop ===
|
|
228
|
+
if (prop === "then") {
|
|
219
229
|
// Make the proxy thenable for async contexts
|
|
220
230
|
return undefined;
|
|
221
231
|
}
|
|
@@ -223,22 +233,27 @@ export class Container {
|
|
|
223
233
|
|
|
224
234
|
// Try to get the value from the resolved instance
|
|
225
235
|
if (placeholder.instance) {
|
|
226
|
-
const value = (
|
|
227
|
-
|
|
236
|
+
const value = (
|
|
237
|
+
placeholder.instance as Record<string | symbol, unknown>
|
|
238
|
+
)[prop];
|
|
239
|
+
return typeof value === "function"
|
|
240
|
+
? value.bind(placeholder.instance)
|
|
241
|
+
: value;
|
|
228
242
|
}
|
|
229
243
|
|
|
230
244
|
// Return a no-op function for method calls during construction
|
|
231
245
|
return () => undefined;
|
|
232
246
|
},
|
|
233
|
-
|
|
247
|
+
|
|
234
248
|
set: (target: T, prop: string | symbol, value: unknown): boolean => {
|
|
235
249
|
if (placeholder.instance) {
|
|
236
|
-
(placeholder.instance as Record<string | symbol, unknown>)[prop] =
|
|
250
|
+
(placeholder.instance as Record<string | symbol, unknown>)[prop] =
|
|
251
|
+
value;
|
|
237
252
|
return true;
|
|
238
253
|
}
|
|
239
254
|
return false;
|
|
240
255
|
},
|
|
241
|
-
|
|
256
|
+
|
|
242
257
|
has: (target: T, prop: string | symbol): boolean => {
|
|
243
258
|
if (placeholder.instance) {
|
|
244
259
|
return prop in (placeholder.instance as object);
|
|
@@ -376,11 +391,18 @@ export function Inject(token: Token | ForwardRef<Token>): ParameterDecorator {
|
|
|
376
391
|
) => {
|
|
377
392
|
const targetObj = target as object;
|
|
378
393
|
const existingTokens: Array<Token | ForwardRef<Token>> =
|
|
379
|
-
getContainerMetadata<Array<Token | ForwardRef<Token>>>(
|
|
394
|
+
getContainerMetadata<Array<Token | ForwardRef<Token>>>(
|
|
395
|
+
targetObj,
|
|
396
|
+
"inject:tokens",
|
|
397
|
+
) ?? [];
|
|
380
398
|
existingTokens[parameterIndex] = token;
|
|
381
399
|
setContainerMetadata(targetObj, "inject:tokens", existingTokens);
|
|
382
400
|
};
|
|
383
401
|
}
|
|
384
402
|
|
|
385
403
|
// Export getter and setter for use by modules
|
|
386
|
-
export {
|
|
404
|
+
export {
|
|
405
|
+
getContainerMetadata as getInjectTokens,
|
|
406
|
+
setContainerMetadata,
|
|
407
|
+
getContainerMetadata,
|
|
408
|
+
};
|
package/src/context/index.ts
CHANGED
|
@@ -287,7 +287,17 @@ export class Context<V extends ContextVariables = ContextVariables> {
|
|
|
287
287
|
/**
|
|
288
288
|
* Create a new Response with current state
|
|
289
289
|
*/
|
|
290
|
-
newResponse(
|
|
290
|
+
newResponse(
|
|
291
|
+
body:
|
|
292
|
+
| Blob
|
|
293
|
+
| ArrayBuffer
|
|
294
|
+
| FormData
|
|
295
|
+
| URLSearchParams
|
|
296
|
+
| ReadableStream<unknown>
|
|
297
|
+
| string
|
|
298
|
+
| null,
|
|
299
|
+
options?: ResponseInit,
|
|
300
|
+
): Response {
|
|
291
301
|
return new Response(body, {
|
|
292
302
|
status: this._response.status,
|
|
293
303
|
headers: this._response.headers,
|