@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,523 @@
1
+ /**
2
+ * Solid SSR Renderer
3
+ *
4
+ * Provides server-side rendering for Solid components using:
5
+ * - renderToString / renderToStringAsync from solid-js/web
6
+ * - Solid Meta for head management
7
+ * - Async rendering support
8
+ */
9
+
10
+ import type {
11
+ SSRContext,
12
+ SSRElement,
13
+ SSRPage,
14
+ FrameworkSSRRenderer,
15
+ } from "../types.js";
16
+
17
+ // Solid types (dynamically imported)
18
+ interface SolidElement {
19
+ type: unknown;
20
+ props: Record<string, unknown>;
21
+ }
22
+
23
+ interface SolidComponent {
24
+ (props: Record<string, unknown>): SolidElement | null;
25
+ }
26
+
27
+ interface SolidRenderResult {
28
+ html: string;
29
+ }
30
+
31
+ // Head element storage
32
+ let headElements: SSRElement[] = [];
33
+
34
+ /**
35
+ * Reset head elements for a new render
36
+ */
37
+ export function resetHead(): void {
38
+ headElements = [];
39
+ }
40
+
41
+ /**
42
+ * Get collected head elements
43
+ */
44
+ export function getHeadElements(): SSRElement[] {
45
+ return [...headElements];
46
+ }
47
+
48
+ /**
49
+ * Add a head element
50
+ */
51
+ export function addHeadElement(element: SSRElement): void {
52
+ headElements.push(element);
53
+ }
54
+
55
+ /**
56
+ * Solid SSR Renderer implementation
57
+ */
58
+ export class SolidSSRRenderer implements FrameworkSSRRenderer {
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ private solidJs: any = null;
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ private solidWeb: any = null;
63
+ private initialized = false;
64
+
65
+ /**
66
+ * Initialize Solid modules
67
+ */
68
+ async init(): Promise<void> {
69
+ if (this.initialized) return;
70
+
71
+ try {
72
+ this.solidJs = await import("solid-js");
73
+ this.solidWeb = await import("solid-js/web");
74
+ this.initialized = true;
75
+ } catch (error) {
76
+ throw new Error(
77
+ "Solid is not installed. Install it with: bun add solid-js"
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Render a component to HTML string
84
+ */
85
+ async renderToString(component: unknown, context: SSRContext): Promise<string> {
86
+ await this.init();
87
+
88
+ if (!this.solidWeb) {
89
+ throw new Error("Solid Web not initialized");
90
+ }
91
+
92
+ resetHead();
93
+
94
+ try {
95
+ const { renderToString } = this.solidWeb;
96
+
97
+ // Create a wrapper that provides context
98
+ const wrappedComponent = this.wrapWithContext(component, context);
99
+
100
+ // Use synchronous renderToString
101
+ const html = renderToString(() => wrappedComponent);
102
+ return html;
103
+ } catch (error) {
104
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
105
+ throw new Error(`Solid renderToString failed: ${errorMessage}`);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Render a component to a stream
111
+ */
112
+ renderToStream(component: unknown, context: SSRContext): ReadableStream<Uint8Array> {
113
+ const encoder = new TextEncoder();
114
+
115
+ return new ReadableStream<Uint8Array>({
116
+ start: async (controller) => {
117
+ try {
118
+ await this.init();
119
+
120
+ if (!this.solidWeb) {
121
+ controller.error(new Error("Solid Web not initialized"));
122
+ return;
123
+ }
124
+
125
+ resetHead();
126
+
127
+ const { renderToStringAsync, renderToStream } = this.solidWeb;
128
+
129
+ // Create a wrapper that provides context
130
+ const wrappedComponent = this.wrapWithContext(component, context);
131
+
132
+ // Check if renderToStream is available
133
+ if (renderToStream) {
134
+ const stream = renderToStream(() => wrappedComponent);
135
+ const reader = stream.getReader();
136
+
137
+ while (true) {
138
+ const { done, value } = await reader.read();
139
+ if (done) {
140
+ controller.close();
141
+ break;
142
+ }
143
+ controller.enqueue(value);
144
+ }
145
+ } else if (renderToStringAsync) {
146
+ // Fallback to async render
147
+ const html = await renderToStringAsync(() => wrappedComponent);
148
+ controller.enqueue(encoder.encode(html));
149
+ controller.close();
150
+ } else {
151
+ // Final fallback to sync render
152
+ const html = this.solidWeb.renderToString(() => wrappedComponent);
153
+ controller.enqueue(encoder.encode(html));
154
+ controller.close();
155
+ }
156
+ } catch (error) {
157
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
158
+ controller.error(new Error(`Solid renderToStream failed: ${errorMessage}`));
159
+ }
160
+ },
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Get head elements from component
166
+ */
167
+ getHeadElements(context: SSRContext): SSRElement[] {
168
+ return getHeadElements();
169
+ }
170
+
171
+ /**
172
+ * Create the framework-specific component
173
+ */
174
+ createComponent(page: SSRPage, context: SSRContext): unknown {
175
+ return {
176
+ page,
177
+ context,
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Wrap component with context provider
183
+ */
184
+ private wrapWithContext(component: unknown, context: SSRContext): () => unknown {
185
+ return () => {
186
+ // In a real implementation, this would use Solid's context API
187
+ // to provide the SSR context to child components
188
+ if (typeof component === "function") {
189
+ return (component as SolidComponent)({ context, ...context.data });
190
+ }
191
+ return component;
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Render with async support
197
+ */
198
+ async renderAsync(component: unknown, context: SSRContext): Promise<string> {
199
+ await this.init();
200
+
201
+ if (!this.solidWeb) {
202
+ throw new Error("Solid Web not initialized");
203
+ }
204
+
205
+ resetHead();
206
+
207
+ try {
208
+ const { renderToStringAsync } = this.solidWeb;
209
+
210
+ if (!renderToStringAsync) {
211
+ // Fallback to sync render
212
+ return this.renderToString(component, context);
213
+ }
214
+
215
+ const wrappedComponent = this.wrapWithContext(component, context);
216
+ const html = await renderToStringAsync(() => wrappedComponent);
217
+ return html;
218
+ } catch (error) {
219
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
220
+ throw new Error(`Solid renderAsync failed: ${errorMessage}`);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Render with suspense support
226
+ */
227
+ async renderWithSuspense(
228
+ component: unknown,
229
+ context: SSRContext,
230
+ fallback: string = "<div>Loading...</div>"
231
+ ): Promise<string> {
232
+ await this.init();
233
+
234
+ if (!this.solidJs || !this.solidWeb) {
235
+ throw new Error("Solid not initialized");
236
+ }
237
+
238
+ resetHead();
239
+
240
+ try {
241
+ const { Suspense } = this.solidJs;
242
+ const { renderToStringAsync } = this.solidWeb;
243
+
244
+ if (!renderToStringAsync) {
245
+ return this.renderToString(component, context);
246
+ }
247
+
248
+ // Wrap with Suspense
249
+ const wrappedComponent = () => {
250
+ return Suspense({
251
+ fallback,
252
+ get children() {
253
+ return this.wrapWithContext(component, context)();
254
+ },
255
+ });
256
+ };
257
+
258
+ const html = await renderToStringAsync(wrappedComponent);
259
+ return html;
260
+ } catch (error) {
261
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
262
+ throw new Error(`Solid renderWithSuspense failed: ${errorMessage}`);
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Create a Solid SSR renderer
269
+ */
270
+ export function createSolidSSRRenderer(): SolidSSRRenderer {
271
+ return new SolidSSRRenderer();
272
+ }
273
+
274
+ /**
275
+ * Solid Meta-like head management
276
+ */
277
+ export class SolidMeta {
278
+ private static instance: SolidMeta;
279
+ private title = "";
280
+ private metaTags: SSRElement[] = [];
281
+ private linkTags: SSRElement[] = [];
282
+ private scriptTags: SSRElement[] = [];
283
+ private styleTags: SSRElement[] = [];
284
+
285
+ static getInstance(): SolidMeta {
286
+ if (!SolidMeta.instance) {
287
+ SolidMeta.instance = new SolidMeta();
288
+ }
289
+ return SolidMeta.instance;
290
+ }
291
+
292
+ setTitle(title: string): this {
293
+ this.title = title;
294
+ addHeadElement({ tag: "title", attrs: {}, children: [{ tag: "#text", attrs: {}, innerHTML: title }] });
295
+ return this;
296
+ }
297
+
298
+ addMeta(attrs: Record<string, string>): this {
299
+ this.metaTags.push({ tag: "meta", attrs });
300
+ addHeadElement({ tag: "meta", attrs });
301
+ return this;
302
+ }
303
+
304
+ addLink(attrs: Record<string, string>): this {
305
+ this.linkTags.push({ tag: "link", attrs });
306
+ addHeadElement({ tag: "link", attrs });
307
+ return this;
308
+ }
309
+
310
+ addScript(attrs: Record<string, string>, innerHTML?: string): this {
311
+ this.scriptTags.push({ tag: "script", attrs, innerHTML });
312
+ addHeadElement({ tag: "script", attrs, innerHTML });
313
+ return this;
314
+ }
315
+
316
+ addStyle(innerHTML: string, attrs?: Record<string, string>): this {
317
+ this.styleTags.push({ tag: "style", attrs: attrs || {}, innerHTML });
318
+ addHeadElement({ tag: "style", attrs: attrs || {}, innerHTML });
319
+ return this;
320
+ }
321
+
322
+ reset(): void {
323
+ this.title = "";
324
+ this.metaTags = [];
325
+ this.linkTags = [];
326
+ this.scriptTags = [];
327
+ this.styleTags = [];
328
+ resetHead();
329
+ }
330
+
331
+ getTitle(): string {
332
+ return this.title;
333
+ }
334
+
335
+ getMetaTags(): SSRElement[] {
336
+ return [...this.metaTags];
337
+ }
338
+
339
+ getLinkTags(): SSRElement[] {
340
+ return [...this.linkTags];
341
+ }
342
+
343
+ getScriptTags(): SSRElement[] {
344
+ return [...this.scriptTags];
345
+ }
346
+
347
+ getStyleTags(): SSRElement[] {
348
+ return [...this.styleTags];
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Get Solid Meta instance
354
+ */
355
+ export function solidMeta(): SolidMeta {
356
+ return SolidMeta.getInstance();
357
+ }
358
+
359
+ /**
360
+ * Convert SSRElement to HTML string
361
+ */
362
+ export function ssrElementToString(element: SSRElement): string {
363
+ if (element.tag === "#text") {
364
+ return escapeHtml(element.innerHTML || "");
365
+ }
366
+
367
+ const attrs = Object.entries(element.attrs)
368
+ .map(([key, value]) => `${key}="${escapeHtml(value)}"`)
369
+ .join(" ");
370
+
371
+ const openTag = attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
372
+
373
+ if (element.innerHTML) {
374
+ return `${openTag}${element.innerHTML}</${element.tag}>`;
375
+ }
376
+
377
+ if (element.children && element.children.length > 0) {
378
+ const children = element.children.map(ssrElementToString).join("");
379
+ return `${openTag}${children}</${element.tag}>`;
380
+ }
381
+
382
+ // Self-closing tags
383
+ const voidElements = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
384
+ if (voidElements.includes(element.tag)) {
385
+ return attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
386
+ }
387
+
388
+ return `${openTag}</${element.tag}>`;
389
+ }
390
+
391
+ /**
392
+ * Escape HTML special characters
393
+ */
394
+ function escapeHtml(str: string): string {
395
+ return str
396
+ .replace(/&/g, "\x26amp;")
397
+ .replace(/</g, "\x26lt;")
398
+ .replace(/>/g, "\x26gt;")
399
+ .replace(/"/g, "\x26quot;")
400
+ .replace(/'/g, "&#39;");
401
+ }
402
+
403
+ /**
404
+ * Create Solid SSR context
405
+ */
406
+ export function createSolidSSRContext(
407
+ request: Request,
408
+ initialState: Record<string, unknown> = {}
409
+ ): SSRContext {
410
+ const url = new URL(request.url);
411
+
412
+ return {
413
+ url: request.url,
414
+ request,
415
+ headers: new Headers(),
416
+ status: 200,
417
+ head: [],
418
+ body: [],
419
+ data: initialState,
420
+ modules: new Set(),
421
+ pathname: url.pathname,
422
+ query: url.searchParams,
423
+ params: {},
424
+ };
425
+ }
426
+
427
+ /**
428
+ * Solid Start-like route data helper
429
+ */
430
+ export function createRouteData<T>(
431
+ fetcher: () => Promise<T>,
432
+ options: {
433
+ key?: () => unknown[];
434
+ deferStream?: boolean;
435
+ } = {}
436
+ ): {
437
+ data: T | undefined;
438
+ loading: boolean;
439
+ error: Error | null;
440
+ } {
441
+ let data: T | undefined;
442
+ let loading = true;
443
+ let error: Error | null = null;
444
+
445
+ // Start fetching
446
+ fetcher()
447
+ .then((result) => {
448
+ data = result;
449
+ loading = false;
450
+ })
451
+ .catch((err) => {
452
+ error = err instanceof Error ? err : new Error(String(err));
453
+ loading = false;
454
+ });
455
+
456
+ return { data, loading, error };
457
+ }
458
+
459
+ /**
460
+ * Solid resource wrapper for SSR
461
+ */
462
+ export class SolidResource<T> {
463
+ private data: T | undefined;
464
+ private error: Error | null = null;
465
+ private loading = true;
466
+ private promise: Promise<T> | null = null;
467
+
468
+ constructor(fetcher: () => Promise<T>) {
469
+ this.promise = fetcher();
470
+ }
471
+
472
+ async load(): Promise<T> {
473
+ if (this.data !== undefined) return this.data;
474
+ if (this.error) throw this.error;
475
+
476
+ try {
477
+ this.data = (await this.promise) as T;
478
+ this.loading = false;
479
+ return this.data;
480
+ } catch (err) {
481
+ this.error = err instanceof Error ? err : new Error(String(err));
482
+ this.loading = false;
483
+ throw this.error;
484
+ }
485
+ }
486
+
487
+ get(): T | undefined {
488
+ return this.data;
489
+ }
490
+
491
+ getState(): { data: T | undefined; loading: boolean; error: Error | null } {
492
+ return {
493
+ data: this.data,
494
+ loading: this.loading,
495
+ error: this.error,
496
+ };
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Create a Solid resource
502
+ */
503
+ export function createSolidResource<T>(fetcher: () => Promise<T>): SolidResource<T> {
504
+ return new SolidResource(fetcher);
505
+ }
506
+
507
+ /**
508
+ * No hydration marker for solid
509
+ */
510
+ export const NO_HYDRATE = "data-no-hydrate";
511
+
512
+ /**
513
+ * Mark element as no-hydrate
514
+ */
515
+ export function noHydrate(element: SSRElement): SSRElement {
516
+ return {
517
+ ...element,
518
+ attrs: {
519
+ ...element.attrs,
520
+ [NO_HYDRATE]: "",
521
+ },
522
+ };
523
+ }