@buenojs/bueno 0.8.4 → 0.8.6
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 +264 -17
- package/dist/cli/{index.js → bin.js} +413 -332
- package/dist/container/index.js +273 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/graphql/index.js +2156 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +9694 -5047
- 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 +3411 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +795 -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/llms.txt +231 -0
- package/package.json +125 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/ARCHITECTURE.md +3 -3
- 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 +15 -15
- 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 +566 -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/graphql/built-in-engine.ts +598 -0
- package/src/graphql/context-builder.ts +110 -0
- package/src/graphql/decorators.ts +358 -0
- package/src/graphql/execution-pipeline.ts +227 -0
- package/src/graphql/graphql-module.ts +563 -0
- package/src/graphql/index.ts +101 -0
- package/src/graphql/metadata.ts +237 -0
- package/src/graphql/schema-builder.ts +319 -0
- package/src/graphql/subscription-handler.ts +283 -0
- package/src/graphql/types.ts +324 -0
- 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 +182 -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 +457 -299
- 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/cli.test.ts +19 -19
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/cli.test.ts +1 -1
- 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/graphql.test.ts +991 -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/frontend/ssr/vue.ts
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
|
+
FrameworkSSRRenderer,
|
|
11
12
|
SSRContext,
|
|
12
13
|
SSRElement,
|
|
13
14
|
SSRPage,
|
|
14
|
-
FrameworkSSRRenderer,
|
|
15
15
|
} from "../types.js";
|
|
16
16
|
|
|
17
17
|
// Vue types (dynamically imported)
|
|
@@ -21,7 +21,9 @@ interface VueApp {
|
|
|
21
21
|
provide(key: string | symbol, value: unknown): VueApp;
|
|
22
22
|
config: {
|
|
23
23
|
globalProperties: Record<string, unknown>;
|
|
24
|
-
errorHandler:
|
|
24
|
+
errorHandler:
|
|
25
|
+
| ((err: unknown, instance: unknown, info: string) => void)
|
|
26
|
+
| null;
|
|
25
27
|
};
|
|
26
28
|
mount(selector: string): unknown;
|
|
27
29
|
unmount(): void;
|
|
@@ -104,9 +106,7 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
104
106
|
this.vueServerRenderer = await import("vue/server-renderer");
|
|
105
107
|
this.initialized = true;
|
|
106
108
|
} catch (error) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
"Vue is not installed. Install it with: bun add vue"
|
|
109
|
-
);
|
|
109
|
+
throw new Error("Vue is not installed. Install it with: bun add vue");
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -134,7 +134,10 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
134
134
|
/**
|
|
135
135
|
* Render a component to HTML string
|
|
136
136
|
*/
|
|
137
|
-
async renderToString(
|
|
137
|
+
async renderToString(
|
|
138
|
+
component: unknown,
|
|
139
|
+
context: SSRContext,
|
|
140
|
+
): Promise<string> {
|
|
138
141
|
await this.init();
|
|
139
142
|
|
|
140
143
|
if (!this.vueServerRenderer) {
|
|
@@ -161,7 +164,8 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
161
164
|
const html = await this.vueServerRenderer.renderToString(app);
|
|
162
165
|
return html;
|
|
163
166
|
} catch (error) {
|
|
164
|
-
const errorMessage =
|
|
167
|
+
const errorMessage =
|
|
168
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
165
169
|
throw new Error(`Vue renderToString failed: ${errorMessage}`);
|
|
166
170
|
}
|
|
167
171
|
}
|
|
@@ -169,7 +173,10 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
169
173
|
/**
|
|
170
174
|
* Render a component to a stream
|
|
171
175
|
*/
|
|
172
|
-
renderToStream(
|
|
176
|
+
renderToStream(
|
|
177
|
+
component: unknown,
|
|
178
|
+
context: SSRContext,
|
|
179
|
+
): ReadableStream<Uint8Array> {
|
|
173
180
|
const encoder = new TextEncoder();
|
|
174
181
|
|
|
175
182
|
return new ReadableStream<Uint8Array>({
|
|
@@ -212,8 +219,11 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
212
219
|
controller.close();
|
|
213
220
|
}
|
|
214
221
|
} catch (error) {
|
|
215
|
-
const errorMessage =
|
|
216
|
-
|
|
222
|
+
const errorMessage =
|
|
223
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
224
|
+
controller.error(
|
|
225
|
+
new Error(`Vue renderToStream failed: ${errorMessage}`),
|
|
226
|
+
);
|
|
217
227
|
}
|
|
218
228
|
},
|
|
219
229
|
});
|
|
@@ -281,7 +291,7 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
281
291
|
return { router: router as unknown as VueRouter, app };
|
|
282
292
|
} catch (error) {
|
|
283
293
|
throw new Error(
|
|
284
|
-
"Vue Router is not installed. Install it with: bun add vue-router"
|
|
294
|
+
"Vue Router is not installed. Install it with: bun add vue-router",
|
|
285
295
|
);
|
|
286
296
|
}
|
|
287
297
|
}
|
|
@@ -293,7 +303,7 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
293
303
|
app: VueApp,
|
|
294
304
|
router: VueRouter,
|
|
295
305
|
url: string,
|
|
296
|
-
context: SSRContext
|
|
306
|
+
context: SSRContext,
|
|
297
307
|
): Promise<string> {
|
|
298
308
|
await this.init();
|
|
299
309
|
|
|
@@ -315,7 +325,8 @@ export class VueSSRRenderer implements FrameworkSSRRenderer {
|
|
|
315
325
|
const html = await this.vueServerRenderer.renderToString(app);
|
|
316
326
|
return html;
|
|
317
327
|
} catch (error) {
|
|
318
|
-
const errorMessage =
|
|
328
|
+
const errorMessage =
|
|
329
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
319
330
|
throw new Error(`Vue renderWithRouter failed: ${errorMessage}`);
|
|
320
331
|
}
|
|
321
332
|
}
|
|
@@ -348,7 +359,11 @@ export class VueMeta {
|
|
|
348
359
|
|
|
349
360
|
setTitle(title: string): this {
|
|
350
361
|
this.title = title;
|
|
351
|
-
addHeadElement({
|
|
362
|
+
addHeadElement({
|
|
363
|
+
tag: "title",
|
|
364
|
+
attrs: {},
|
|
365
|
+
children: [{ tag: "#text", attrs: {}, innerHTML: title }],
|
|
366
|
+
});
|
|
352
367
|
return this;
|
|
353
368
|
}
|
|
354
369
|
|
|
@@ -437,7 +452,22 @@ export function ssrElementToString(element: SSRElement): string {
|
|
|
437
452
|
}
|
|
438
453
|
|
|
439
454
|
// Self-closing tags
|
|
440
|
-
const voidElements = [
|
|
455
|
+
const voidElements = [
|
|
456
|
+
"area",
|
|
457
|
+
"base",
|
|
458
|
+
"br",
|
|
459
|
+
"col",
|
|
460
|
+
"embed",
|
|
461
|
+
"hr",
|
|
462
|
+
"img",
|
|
463
|
+
"input",
|
|
464
|
+
"link",
|
|
465
|
+
"meta",
|
|
466
|
+
"param",
|
|
467
|
+
"source",
|
|
468
|
+
"track",
|
|
469
|
+
"wbr",
|
|
470
|
+
];
|
|
441
471
|
if (voidElements.includes(element.tag)) {
|
|
442
472
|
return attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
|
|
443
473
|
}
|
|
@@ -476,7 +506,9 @@ export function useHead(head: {
|
|
|
476
506
|
head.meta?.forEach((attrs) => meta.addMeta(attrs));
|
|
477
507
|
head.link?.forEach((attrs) => meta.addLink(attrs));
|
|
478
508
|
head.script?.forEach((attrs) => meta.addScript(attrs));
|
|
479
|
-
head.style?.forEach(({ innerHTML, attrs }) =>
|
|
509
|
+
head.style?.forEach(({ innerHTML, attrs }) =>
|
|
510
|
+
meta.addStyle(innerHTML, attrs),
|
|
511
|
+
);
|
|
480
512
|
}
|
|
481
513
|
|
|
482
514
|
/**
|
|
@@ -484,7 +516,7 @@ export function useHead(head: {
|
|
|
484
516
|
*/
|
|
485
517
|
export function createVueSSRContext(
|
|
486
518
|
request: Request,
|
|
487
|
-
initialState: Record<string, unknown> = {}
|
|
519
|
+
initialState: Record<string, unknown> = {},
|
|
488
520
|
): SSRContext {
|
|
489
521
|
const url = new URL(request.url);
|
|
490
522
|
|
|
@@ -501,4 +533,4 @@ export function createVueSSRContext(
|
|
|
501
533
|
query: url.searchParams,
|
|
502
534
|
params: {},
|
|
503
535
|
};
|
|
504
|
-
}
|
|
536
|
+
}
|
package/src/frontend/ssr.ts
CHANGED
|
@@ -9,25 +9,28 @@
|
|
|
9
9
|
* - Head management (title, meta, links)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { type Logger, createLogger } from "../logger/index.js";
|
|
13
|
+
import { type ReactSSRRenderer, createReactSSRRenderer } from "./ssr/react.js";
|
|
14
|
+
import { type SolidSSRRenderer, createSolidSSRRenderer } from "./ssr/solid.js";
|
|
15
|
+
import {
|
|
16
|
+
type SvelteSSRRenderer,
|
|
17
|
+
createSvelteSSRRenderer,
|
|
18
|
+
} from "./ssr/svelte.js";
|
|
19
|
+
import { type VueSSRRenderer, createVueSSRRenderer } from "./ssr/vue.js";
|
|
13
20
|
import type {
|
|
14
|
-
|
|
21
|
+
BuildManifest,
|
|
22
|
+
FrameworkSSRRenderer,
|
|
23
|
+
FrontendFramework,
|
|
15
24
|
PartialSSRConfig,
|
|
25
|
+
PreloadLink,
|
|
26
|
+
RenderResult,
|
|
27
|
+
SSRConfig,
|
|
16
28
|
SSRContext,
|
|
17
29
|
SSRElement,
|
|
18
|
-
SSRPage,
|
|
19
|
-
RenderResult,
|
|
20
30
|
SSRHydrationData,
|
|
31
|
+
SSRPage,
|
|
21
32
|
SSRRenderOptions,
|
|
22
|
-
BuildManifest,
|
|
23
|
-
FrontendFramework,
|
|
24
|
-
FrameworkSSRRenderer,
|
|
25
|
-
PreloadLink,
|
|
26
33
|
} from "./types.js";
|
|
27
|
-
import { createReactSSRRenderer, type ReactSSRRenderer } from "./ssr/react.js";
|
|
28
|
-
import { createVueSSRRenderer, type VueSSRRenderer } from "./ssr/vue.js";
|
|
29
|
-
import { createSvelteSSRRenderer, type SvelteSSRRenderer } from "./ssr/svelte.js";
|
|
30
|
-
import { createSolidSSRRenderer, type SolidSSRRenderer } from "./ssr/solid.js";
|
|
31
34
|
|
|
32
35
|
// ============= Constants =============
|
|
33
36
|
|
|
@@ -39,7 +42,7 @@ const DEFAULT_BUFFER_INITIAL_STREAM = true;
|
|
|
39
42
|
|
|
40
43
|
/**
|
|
41
44
|
* Main SSR Renderer class
|
|
42
|
-
*
|
|
45
|
+
*
|
|
43
46
|
* Provides server-side rendering for all supported frameworks
|
|
44
47
|
* with streaming support and client hydration.
|
|
45
48
|
*/
|
|
@@ -68,7 +71,8 @@ export class SSRRenderer {
|
|
|
68
71
|
clientManifest: config.clientManifest,
|
|
69
72
|
streaming: config.streaming ?? DEFAULT_STREAMING,
|
|
70
73
|
maxTimeout: config.maxTimeout ?? DEFAULT_MAX_TIMEOUT,
|
|
71
|
-
bufferInitialStream:
|
|
74
|
+
bufferInitialStream:
|
|
75
|
+
config.bufferInitialStream ?? DEFAULT_BUFFER_INITIAL_STREAM,
|
|
72
76
|
framework: config.framework,
|
|
73
77
|
rootDir: config.rootDir,
|
|
74
78
|
template: config.template,
|
|
@@ -80,14 +84,18 @@ export class SSRRenderer {
|
|
|
80
84
|
* Initialize the framework-specific renderer
|
|
81
85
|
*/
|
|
82
86
|
async init(): Promise<void> {
|
|
83
|
-
this.frameworkRenderer = await this.createFrameworkRenderer(
|
|
87
|
+
this.frameworkRenderer = await this.createFrameworkRenderer(
|
|
88
|
+
this.config.framework,
|
|
89
|
+
);
|
|
84
90
|
this.logger.info(`SSR initialized for framework: ${this.config.framework}`);
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
/**
|
|
88
94
|
* Create framework-specific renderer
|
|
89
95
|
*/
|
|
90
|
-
private async createFrameworkRenderer(
|
|
96
|
+
private async createFrameworkRenderer(
|
|
97
|
+
framework: FrontendFramework,
|
|
98
|
+
): Promise<FrameworkSSRRenderer> {
|
|
91
99
|
switch (framework) {
|
|
92
100
|
case "react":
|
|
93
101
|
const reactRenderer = createReactSSRRenderer();
|
|
@@ -124,7 +132,7 @@ export class SSRRenderer {
|
|
|
124
132
|
try {
|
|
125
133
|
// Load the page module
|
|
126
134
|
const page = await this.loadPage(url);
|
|
127
|
-
|
|
135
|
+
|
|
128
136
|
// Run server-side data fetching if available
|
|
129
137
|
if (page.getServerSideProps) {
|
|
130
138
|
this.logger.debug(`Fetching server-side props for: ${url}`);
|
|
@@ -134,7 +142,10 @@ export class SSRRenderer {
|
|
|
134
142
|
|
|
135
143
|
// Render the page
|
|
136
144
|
const component = this.frameworkRenderer!.createComponent(page, context);
|
|
137
|
-
const html = await this.frameworkRenderer!.renderToString(
|
|
145
|
+
const html = await this.frameworkRenderer!.renderToString(
|
|
146
|
+
component,
|
|
147
|
+
context,
|
|
148
|
+
);
|
|
138
149
|
|
|
139
150
|
// Get head elements
|
|
140
151
|
const headElements = this.frameworkRenderer!.getHeadElements(context);
|
|
@@ -155,9 +166,10 @@ export class SSRRenderer {
|
|
|
155
166
|
status: context.status,
|
|
156
167
|
};
|
|
157
168
|
} catch (error) {
|
|
158
|
-
const errorMessage =
|
|
169
|
+
const errorMessage =
|
|
170
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
159
171
|
this.logger.error(`SSR render failed for ${url}:`, error);
|
|
160
|
-
|
|
172
|
+
|
|
161
173
|
return {
|
|
162
174
|
html: this.renderErrorPage(error),
|
|
163
175
|
head: "<title>Error</title>",
|
|
@@ -202,7 +214,10 @@ export class SSRRenderer {
|
|
|
202
214
|
|
|
203
215
|
// Create component and render to stream
|
|
204
216
|
const component = frameworkRenderer!.createComponent(page, context);
|
|
205
|
-
const htmlStream = frameworkRenderer!.renderToStream(
|
|
217
|
+
const htmlStream = frameworkRenderer!.renderToStream(
|
|
218
|
+
component,
|
|
219
|
+
context,
|
|
220
|
+
);
|
|
206
221
|
|
|
207
222
|
const reader = htmlStream.getReader();
|
|
208
223
|
while (true) {
|
|
@@ -216,7 +231,8 @@ export class SSRRenderer {
|
|
|
216
231
|
controller.enqueue(encoder.encode(footer));
|
|
217
232
|
controller.close();
|
|
218
233
|
} catch (error) {
|
|
219
|
-
const errorMessage =
|
|
234
|
+
const errorMessage =
|
|
235
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
220
236
|
this.logger.error(`SSR stream failed for ${url}:`, error);
|
|
221
237
|
controller.enqueue(encoder.encode(this.renderErrorPage(error)));
|
|
222
238
|
controller.close();
|
|
@@ -229,7 +245,13 @@ export class SSRRenderer {
|
|
|
229
245
|
* Render with options
|
|
230
246
|
*/
|
|
231
247
|
async renderWithOptions(options: SSRRenderOptions): Promise<RenderResult> {
|
|
232
|
-
const {
|
|
248
|
+
const {
|
|
249
|
+
url,
|
|
250
|
+
request,
|
|
251
|
+
params = {},
|
|
252
|
+
props = {},
|
|
253
|
+
skipStreaming = false,
|
|
254
|
+
} = options;
|
|
233
255
|
|
|
234
256
|
if (!skipStreaming && this.config.streaming) {
|
|
235
257
|
// For streaming, we need to buffer the result
|
|
@@ -249,7 +271,7 @@ export class SSRRenderer {
|
|
|
249
271
|
combined.set(acc);
|
|
250
272
|
combined.set(chunk, acc.length);
|
|
251
273
|
return combined;
|
|
252
|
-
}, new Uint8Array())
|
|
274
|
+
}, new Uint8Array()),
|
|
253
275
|
);
|
|
254
276
|
|
|
255
277
|
return {
|
|
@@ -448,12 +470,29 @@ export class SSRRenderer {
|
|
|
448
470
|
}
|
|
449
471
|
|
|
450
472
|
if (element.children && element.children.length > 0) {
|
|
451
|
-
const children = element.children
|
|
473
|
+
const children = element.children
|
|
474
|
+
.map(this.ssrElementToString.bind(this))
|
|
475
|
+
.join("");
|
|
452
476
|
return `${openTag}${children}</${element.tag}>`;
|
|
453
477
|
}
|
|
454
478
|
|
|
455
479
|
// Self-closing tags
|
|
456
|
-
const voidElements = [
|
|
480
|
+
const voidElements = [
|
|
481
|
+
"area",
|
|
482
|
+
"base",
|
|
483
|
+
"br",
|
|
484
|
+
"col",
|
|
485
|
+
"embed",
|
|
486
|
+
"hr",
|
|
487
|
+
"img",
|
|
488
|
+
"input",
|
|
489
|
+
"link",
|
|
490
|
+
"meta",
|
|
491
|
+
"param",
|
|
492
|
+
"source",
|
|
493
|
+
"track",
|
|
494
|
+
"wbr",
|
|
495
|
+
];
|
|
457
496
|
if (voidElements.includes(element.tag)) {
|
|
458
497
|
return attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
|
|
459
498
|
}
|
|
@@ -520,9 +559,10 @@ export class SSRRenderer {
|
|
|
520
559
|
*/
|
|
521
560
|
private renderErrorPage(error: unknown): string {
|
|
522
561
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
523
|
-
const stack =
|
|
524
|
-
|
|
525
|
-
|
|
562
|
+
const stack =
|
|
563
|
+
error instanceof Error && process.env.NODE_ENV !== "production"
|
|
564
|
+
? `<pre>${this.escapeHtml(error.stack || "")}</pre>`
|
|
565
|
+
: "";
|
|
526
566
|
|
|
527
567
|
return `<!DOCTYPE html>
|
|
528
568
|
<html>
|
|
@@ -588,7 +628,7 @@ export function createSSRRenderer(config: PartialSSRConfig): SSRRenderer {
|
|
|
588
628
|
*/
|
|
589
629
|
export function createSSRContext(
|
|
590
630
|
request: Request,
|
|
591
|
-
params: Record<string, string> = {}
|
|
631
|
+
params: Record<string, string> = {},
|
|
592
632
|
): SSRContext {
|
|
593
633
|
const url = new URL(request.url);
|
|
594
634
|
|
|
@@ -674,13 +714,14 @@ export function mergeHeadElements(...arrays: SSRElement[][]): SSRElement[] {
|
|
|
674
714
|
for (const arr of arrays) {
|
|
675
715
|
for (const element of arr) {
|
|
676
716
|
// Create a key based on tag and identifying attributes
|
|
677
|
-
const key =
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
717
|
+
const key =
|
|
718
|
+
element.tag === "title"
|
|
719
|
+
? "title"
|
|
720
|
+
: element.tag === "meta" && element.attrs.name
|
|
721
|
+
? `meta:${element.attrs.name}`
|
|
722
|
+
: element.tag === "meta" && element.attrs.property
|
|
723
|
+
? `meta:${element.attrs.property}`
|
|
724
|
+
: JSON.stringify(element);
|
|
684
725
|
|
|
685
726
|
if (!seen.has(key)) {
|
|
686
727
|
seen.add(key);
|
|
@@ -695,5 +736,8 @@ export function mergeHeadElements(...arrays: SSRElement[][]): SSRElement[] {
|
|
|
695
736
|
// Re-export framework renderers
|
|
696
737
|
export { createReactSSRRenderer, type ReactSSRRenderer } from "./ssr/react.js";
|
|
697
738
|
export { createVueSSRRenderer, type VueSSRRenderer } from "./ssr/vue.js";
|
|
698
|
-
export {
|
|
699
|
-
|
|
739
|
+
export {
|
|
740
|
+
createSvelteSSRRenderer,
|
|
741
|
+
type SvelteSSRRenderer,
|
|
742
|
+
} from "./ssr/svelte.js";
|
|
743
|
+
export { createSolidSSRRenderer, type SolidSSRRenderer } from "./ssr/solid.js";
|