@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,546 @@
1
+ /**
2
+ * Svelte SSR Renderer
3
+ *
4
+ * Provides server-side rendering for Svelte components using:
5
+ * - render() from svelte/server
6
+ * - Svelte Head component support
7
+ * - SvelteKit-like SSR patterns
8
+ */
9
+
10
+ import type {
11
+ SSRContext,
12
+ SSRElement,
13
+ SSRPage,
14
+ FrameworkSSRRenderer,
15
+ } from "../types.js";
16
+
17
+ // Svelte types (dynamically imported)
18
+ interface SvelteComponent {
19
+ render(props?: Record<string, unknown>): SvelteRenderResult;
20
+ $$render(result: string, props: Record<string, unknown>, bindings: unknown, context: unknown): string;
21
+ }
22
+
23
+ interface SvelteRenderResult {
24
+ html: string;
25
+ head: string;
26
+ css: {
27
+ code: string;
28
+ map: unknown;
29
+ };
30
+ }
31
+
32
+ interface SvelteComponentConstructor {
33
+ new (options: {
34
+ target: object | null;
35
+ props?: Record<string, unknown>;
36
+ hydrate?: boolean;
37
+ intro?: boolean;
38
+ $inline?: boolean;
39
+ }): SvelteComponent;
40
+
41
+ render(props?: Record<string, unknown>): SvelteRenderResult;
42
+ }
43
+
44
+ // Head element storage
45
+ let headElements: SSRElement[] = [];
46
+ let headString = "";
47
+
48
+ /**
49
+ * Reset head elements for a new render
50
+ */
51
+ export function resetHead(): void {
52
+ headElements = [];
53
+ headString = "";
54
+ }
55
+
56
+ /**
57
+ * Get collected head elements
58
+ */
59
+ export function getHeadElements(): SSRElement[] {
60
+ return [...headElements];
61
+ }
62
+
63
+ /**
64
+ * Get head string from Svelte render
65
+ */
66
+ export function getHeadString(): string {
67
+ return headString;
68
+ }
69
+
70
+ /**
71
+ * Set head string from Svelte render
72
+ */
73
+ export function setHeadString(head: string): void {
74
+ headString = head;
75
+ }
76
+
77
+ /**
78
+ * Add a head element
79
+ */
80
+ export function addHeadElement(element: SSRElement): void {
81
+ headElements.push(element);
82
+ }
83
+
84
+ /**
85
+ * Svelte SSR Renderer implementation
86
+ */
87
+ export class SvelteSSRRenderer implements FrameworkSSRRenderer {
88
+ private svelteServer: typeof import("svelte/server") | null = null;
89
+ private initialized = false;
90
+
91
+ /**
92
+ * Initialize Svelte server module
93
+ */
94
+ async init(): Promise<void> {
95
+ if (this.initialized) return;
96
+
97
+ try {
98
+ this.svelteServer = await import("svelte/server");
99
+ this.initialized = true;
100
+ } catch (error) {
101
+ throw new Error(
102
+ "Svelte is not installed. Install it with: bun add svelte"
103
+ );
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Render a component to HTML string
109
+ */
110
+ async renderToString(component: unknown, context: SSRContext): Promise<string> {
111
+ await this.init();
112
+
113
+ resetHead();
114
+
115
+ try {
116
+ // Check if component has render method (Svelte SSR component)
117
+ if (this.isSvelteComponent(component)) {
118
+ const result = component.render({
119
+ context,
120
+ ...context.data,
121
+ });
122
+
123
+ // Store head content from Svelte <svelte:head>
124
+ if (result.head) {
125
+ setHeadString(result.head);
126
+ this.parseHeadElements(result.head);
127
+ }
128
+
129
+ return result.html;
130
+ }
131
+
132
+ // If it's a constructor, call static render
133
+ if (typeof component === "function" && "render" in component) {
134
+ const result = (component as SvelteComponentConstructor).render({
135
+ context,
136
+ ...context.data,
137
+ });
138
+
139
+ if (result.head) {
140
+ setHeadString(result.head);
141
+ this.parseHeadElements(result.head);
142
+ }
143
+
144
+ return result.html;
145
+ }
146
+
147
+ throw new Error("Invalid Svelte component");
148
+ } catch (error) {
149
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
150
+ throw new Error(`Svelte renderToString failed: ${errorMessage}`);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Render a component to a stream
156
+ */
157
+ renderToStream(component: unknown, context: SSRContext): ReadableStream<Uint8Array> {
158
+ const encoder = new TextEncoder();
159
+
160
+ return new ReadableStream<Uint8Array>({
161
+ start: async (controller) => {
162
+ try {
163
+ await this.init();
164
+
165
+ resetHead();
166
+
167
+ // Svelte doesn't have native streaming, so we render to string
168
+ // and then stream it
169
+ const html = await this.renderToString(component, context);
170
+
171
+ // Simulate streaming by chunking the output
172
+ const chunkSize = 8192; // 8KB chunks
173
+ for (let i = 0; i < html.length; i += chunkSize) {
174
+ const chunk = html.slice(i, i + chunkSize);
175
+ controller.enqueue(encoder.encode(chunk));
176
+ }
177
+
178
+ controller.close();
179
+ } catch (error) {
180
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
181
+ controller.error(new Error(`Svelte renderToStream failed: ${errorMessage}`));
182
+ }
183
+ },
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Get head elements from component
189
+ */
190
+ getHeadElements(context: SSRContext): SSRElement[] {
191
+ return getHeadElements();
192
+ }
193
+
194
+ /**
195
+ * Create the framework-specific component
196
+ */
197
+ createComponent(page: SSRPage, context: SSRContext): unknown {
198
+ return {
199
+ page,
200
+ context,
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Check if object is a Svelte component
206
+ */
207
+ private isSvelteComponent(obj: unknown): obj is SvelteComponent {
208
+ return (
209
+ typeof obj === "object" &&
210
+ obj !== null &&
211
+ "render" in obj &&
212
+ typeof (obj as SvelteComponent).render === "function"
213
+ );
214
+ }
215
+
216
+ /**
217
+ * Parse head HTML string into SSRElement array
218
+ */
219
+ private parseHeadElements(headHtml: string): void {
220
+ // Simple regex-based parsing for common head elements
221
+ const titleMatch = headHtml.match(/<title>([^<]*)<\/title>/);
222
+ if (titleMatch) {
223
+ addHeadElement({
224
+ tag: "title",
225
+ attrs: {},
226
+ children: [{ tag: "#text", attrs: {}, innerHTML: titleMatch[1] }],
227
+ });
228
+ }
229
+
230
+ // Parse meta tags
231
+ const metaRegex = /<meta\s+([^>]*)\/?>/g;
232
+ let metaMatch;
233
+ while ((metaMatch = metaRegex.exec(headHtml)) !== null) {
234
+ const attrs = this.parseAttributes(metaMatch[1]);
235
+ addHeadElement({ tag: "meta", attrs });
236
+ }
237
+
238
+ // Parse link tags
239
+ const linkRegex = /<link\s+([^>]*)\/?>/g;
240
+ let linkMatch;
241
+ while ((linkMatch = linkRegex.exec(headHtml)) !== null) {
242
+ const attrs = this.parseAttributes(linkMatch[1]);
243
+ addHeadElement({ tag: "link", attrs });
244
+ }
245
+
246
+ // Parse style tags
247
+ const styleRegex = /<style([^>]*)>([^<]*)<\/style>/g;
248
+ let styleMatch;
249
+ while ((styleMatch = styleRegex.exec(headHtml)) !== null) {
250
+ const attrs = this.parseAttributes(styleMatch[1]);
251
+ addHeadElement({ tag: "style", attrs, innerHTML: styleMatch[2] });
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Parse HTML attributes string into object
257
+ */
258
+ private parseAttributes(attrString: string): Record<string, string> {
259
+ const attrs: Record<string, string> = {};
260
+ const regex = /(\w+)=["']([^"']*)["']/g;
261
+ let match;
262
+
263
+ while ((match = regex.exec(attrString)) !== null) {
264
+ attrs[match[1]] = match[2];
265
+ }
266
+
267
+ return attrs;
268
+ }
269
+
270
+ /**
271
+ * Render with CSS extraction
272
+ */
273
+ async renderWithCSS(
274
+ component: unknown,
275
+ context: SSRContext
276
+ ): Promise<{
277
+ html: string;
278
+ head: string;
279
+ css: string;
280
+ }> {
281
+ await this.init();
282
+
283
+ resetHead();
284
+
285
+ if (this.isSvelteComponent(component)) {
286
+ const result = component.render({
287
+ context,
288
+ ...context.data,
289
+ });
290
+
291
+ if (result.head) {
292
+ setHeadString(result.head);
293
+ this.parseHeadElements(result.head);
294
+ }
295
+
296
+ return {
297
+ html: result.html,
298
+ head: result.head,
299
+ css: result.css.code,
300
+ };
301
+ }
302
+
303
+ if (typeof component === "function" && "render" in component) {
304
+ const result = (component as SvelteComponentConstructor).render({
305
+ context,
306
+ ...context.data,
307
+ });
308
+
309
+ if (result.head) {
310
+ setHeadString(result.head);
311
+ this.parseHeadElements(result.head);
312
+ }
313
+
314
+ return {
315
+ html: result.html,
316
+ head: result.head,
317
+ css: result.css.code,
318
+ };
319
+ }
320
+
321
+ throw new Error("Invalid Svelte component");
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Create a Svelte SSR renderer
327
+ */
328
+ export function createSvelteSSRRenderer(): SvelteSSRRenderer {
329
+ return new SvelteSSRRenderer();
330
+ }
331
+
332
+ /**
333
+ * Svelte Head component helper
334
+ */
335
+ export class SvelteHead {
336
+ private static instance: SvelteHead;
337
+ private title = "";
338
+ private metaTags: SSRElement[] = [];
339
+ private linkTags: SSRElement[] = [];
340
+ private scriptTags: SSRElement[] = [];
341
+ private styleTags: SSRElement[] = [];
342
+
343
+ static getInstance(): SvelteHead {
344
+ if (!SvelteHead.instance) {
345
+ SvelteHead.instance = new SvelteHead();
346
+ }
347
+ return SvelteHead.instance;
348
+ }
349
+
350
+ setTitle(title: string): this {
351
+ this.title = title;
352
+ addHeadElement({ tag: "title", attrs: {}, children: [{ tag: "#text", attrs: {}, innerHTML: title }] });
353
+ return this;
354
+ }
355
+
356
+ addMeta(attrs: Record<string, string>): this {
357
+ this.metaTags.push({ tag: "meta", attrs });
358
+ addHeadElement({ tag: "meta", attrs });
359
+ return this;
360
+ }
361
+
362
+ addLink(attrs: Record<string, string>): this {
363
+ this.linkTags.push({ tag: "link", attrs });
364
+ addHeadElement({ tag: "link", attrs });
365
+ return this;
366
+ }
367
+
368
+ addScript(attrs: Record<string, string>, innerHTML?: string): this {
369
+ this.scriptTags.push({ tag: "script", attrs, innerHTML });
370
+ addHeadElement({ tag: "script", attrs, innerHTML });
371
+ return this;
372
+ }
373
+
374
+ addStyle(innerHTML: string, attrs?: Record<string, string>): this {
375
+ this.styleTags.push({ tag: "style", attrs: attrs || {}, innerHTML });
376
+ addHeadElement({ tag: "style", attrs: attrs || {}, innerHTML });
377
+ return this;
378
+ }
379
+
380
+ reset(): void {
381
+ this.title = "";
382
+ this.metaTags = [];
383
+ this.linkTags = [];
384
+ this.scriptTags = [];
385
+ this.styleTags = [];
386
+ resetHead();
387
+ }
388
+
389
+ getTitle(): string {
390
+ return this.title;
391
+ }
392
+
393
+ getMetaTags(): SSRElement[] {
394
+ return [...this.metaTags];
395
+ }
396
+
397
+ getLinkTags(): SSRElement[] {
398
+ return [...this.linkTags];
399
+ }
400
+
401
+ getScriptTags(): SSRElement[] {
402
+ return [...this.scriptTags];
403
+ }
404
+
405
+ getStyleTags(): SSRElement[] {
406
+ return [...this.styleTags];
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Get Svelte Head instance
412
+ */
413
+ export function svelteHead(): SvelteHead {
414
+ return SvelteHead.getInstance();
415
+ }
416
+
417
+ /**
418
+ * Convert SSRElement to HTML string
419
+ */
420
+ export function ssrElementToString(element: SSRElement): string {
421
+ if (element.tag === "#text") {
422
+ return escapeHtml(element.innerHTML || "");
423
+ }
424
+
425
+ const attrs = Object.entries(element.attrs)
426
+ .map(([key, value]) => `${key}="${escapeHtml(value)}"`)
427
+ .join(" ");
428
+
429
+ const openTag = attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
430
+
431
+ if (element.innerHTML) {
432
+ return `${openTag}${element.innerHTML}</${element.tag}>`;
433
+ }
434
+
435
+ if (element.children && element.children.length > 0) {
436
+ const children = element.children.map(ssrElementToString).join("");
437
+ return `${openTag}${children}</${element.tag}>`;
438
+ }
439
+
440
+ // Self-closing tags
441
+ const voidElements = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
442
+ if (voidElements.includes(element.tag)) {
443
+ return attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
444
+ }
445
+
446
+ return `${openTag}</${element.tag}>`;
447
+ }
448
+
449
+ /**
450
+ * Escape HTML special characters
451
+ */
452
+ function escapeHtml(str: string): string {
453
+ return str
454
+ .replace(/&/g, "\x26amp;")
455
+ .replace(/</g, "\x26lt;")
456
+ .replace(/>/g, "\x26gt;")
457
+ .replace(/"/g, "\x26quot;")
458
+ .replace(/'/g, "&#39;");
459
+ }
460
+
461
+ /**
462
+ * Load a Svelte component from file path
463
+ */
464
+ export async function loadSvelteComponent(
465
+ filePath: string
466
+ ): Promise<SvelteComponentConstructor> {
467
+ try {
468
+ // Dynamic import of the compiled Svelte component
469
+ const module = await import(filePath);
470
+ return module.default;
471
+ } catch (error) {
472
+ throw new Error(`Failed to load Svelte component: ${filePath}`);
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Create Svelte SSR context
478
+ */
479
+ export function createSvelteSSRContext(
480
+ request: Request,
481
+ initialState: Record<string, unknown> = {}
482
+ ): SSRContext {
483
+ const url = new URL(request.url);
484
+
485
+ return {
486
+ url: request.url,
487
+ request,
488
+ headers: new Headers(),
489
+ status: 200,
490
+ head: [],
491
+ body: [],
492
+ data: initialState,
493
+ modules: new Set(),
494
+ pathname: url.pathname,
495
+ query: url.searchParams,
496
+ params: {},
497
+ };
498
+ }
499
+
500
+ /**
501
+ * SvelteKit-like page store for SSR
502
+ */
503
+ export class SveltePageStore {
504
+ private static instance: SveltePageStore;
505
+ private state: {
506
+ url: URL;
507
+ params: Record<string, string>;
508
+ route: { id: string };
509
+ status: number;
510
+ error: Error | null;
511
+ data: Record<string, unknown>;
512
+ } | null = null;
513
+
514
+ static getInstance(): SveltePageStore {
515
+ if (!SveltePageStore.instance) {
516
+ SveltePageStore.instance = new SveltePageStore();
517
+ }
518
+ return SveltePageStore.instance;
519
+ }
520
+
521
+ set(context: SSRContext): void {
522
+ this.state = {
523
+ url: new URL(context.url),
524
+ params: context.params,
525
+ route: { id: context.pathname },
526
+ status: context.status,
527
+ error: null,
528
+ data: context.data,
529
+ };
530
+ }
531
+
532
+ get(): typeof this.state {
533
+ return this.state;
534
+ }
535
+
536
+ reset(): void {
537
+ this.state = null;
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Get Svelte page store
543
+ */
544
+ export function getPageStore(): SveltePageStore {
545
+ return SveltePageStore.getInstance();
546
+ }