@absolutejs/absolute 0.19.0-beta.176 → 0.19.0-beta.178

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 (40) hide show
  1. package/.absolutejs/eslint-cache +1 -1
  2. package/.absolutejs/tsconfig.tsbuildinfo +1 -1
  3. package/ROADMAP.md +191 -191
  4. package/dist/Image-fpjk72vg.vue +140 -0
  5. package/dist/src/angular/components/image.component.d.ts +33 -0
  6. package/dist/src/angular/components/index.d.ts +1 -0
  7. package/dist/src/angular/components/index.js +395 -0
  8. package/dist/src/angular/components/index.js.map +11 -0
  9. package/dist/{angular → src/angular}/index.js +11 -1
  10. package/dist/{angular → src/angular}/index.js.map +2 -2
  11. package/dist/src/build/optimizeHtmlImages.d.ts +2 -0
  12. package/dist/{build.js → src/build.js} +496 -243
  13. package/dist/{build.js.map → src/build.js.map} +7 -5
  14. package/dist/{index.js → src/index.js} +746 -276
  15. package/dist/{index.js.map → src/index.js.map} +9 -6
  16. package/dist/src/plugins/imageOptimizer.d.ts +2 -0
  17. package/dist/src/react/components/Image.d.ts +2 -0
  18. package/dist/src/react/components/index.d.ts +1 -0
  19. package/dist/{react → src/react}/hooks/index.js +11 -1
  20. package/dist/{react → src/react}/hooks/index.js.map +2 -2
  21. package/dist/{react → src/react}/index.js +11 -1
  22. package/dist/{react → src/react}/index.js.map +2 -2
  23. package/dist/{svelte → src/svelte}/index.js +11 -1
  24. package/dist/{svelte → src/svelte}/index.js.map +2 -2
  25. package/dist/src/utils/imageProcessing.d.ts +33 -0
  26. package/dist/src/vue/components/index.d.ts +1 -0
  27. package/dist/src/vue/components/index.js +84 -0
  28. package/dist/src/vue/components/index.js.map +9 -0
  29. package/dist/{vue → src/vue}/index.js +11 -1
  30. package/dist/{vue → src/vue}/index.js.map +2 -2
  31. package/dist/svelte/components/Head.svelte +147 -0
  32. package/dist/svelte/components/Image.svelte +158 -0
  33. package/dist/svelte/components/JsonLd.svelte +20 -0
  34. package/dist/types/build.d.ts +2 -0
  35. package/dist/types/image.d.ts +77 -0
  36. package/dist/types/index.d.ts +1 -0
  37. package/package.json +19 -4
  38. package/types/build.ts +3 -0
  39. package/types/image.ts +91 -0
  40. package/types/index.ts +1 -0
package/ROADMAP.md CHANGED
@@ -4,7 +4,7 @@ Features missing from AbsoluteJS that Next.js provides, ordered by priority. Eac
4
4
 
5
5
  ---
6
6
 
7
- ## P1 — Image Optimization
7
+ ## 1. P1 — Image Optimization
8
8
 
9
9
  **What Next.js does:**
10
10
  `<Image>` component that automatically converts images to WebP/AVIF, generates responsive `srcset` attributes, lazy loads with blur placeholders, and serves optimized images through an on-demand image optimization API route. Prevents layout shift with required width/height.
@@ -26,7 +26,7 @@ Nothing. Images are served as-is from the public/assets directory.
26
26
 
27
27
  ---
28
28
 
29
- ## P1 — Loading / Error / Not-Found States
29
+ ## 2. P1 — Loading / Error / Not-Found States
30
30
 
31
31
  **What Next.js does:**
32
32
  Per-route-segment `loading.tsx` (shows during async data fetch), `error.tsx` (catches runtime errors with React error boundary), and `not-found.tsx` (404 page). These are automatic — drop the file in and it works.
@@ -51,7 +51,7 @@ Per-route-segment `loading.tsx` (shows during async data fetch), `error.tsx` (ca
51
51
 
52
52
  ---
53
53
 
54
- ## P1 — Client-Side Navigation / SPA Mode with `<Link>`
54
+ ## 3. P1 — Client-Side Navigation / SPA Mode with `<Link>`
55
55
 
56
56
  **What Next.js does:**
57
57
  `<Link>` component that intercepts clicks and does client-side navigation — fetches only the new page's data/RSC payload, swaps the content, and preserves layout state (scroll position, open menus, form inputs). Prefetches linked pages on hover or when they enter the viewport. This is what makes Next.js apps feel like SPAs even though they're server-rendered.
@@ -123,7 +123,7 @@ React Router and similar client-side routers can't help here — they swap clien
123
123
 
124
124
  ---
125
125
 
126
- ## P1 — Islands Architecture (Multi-Framework Pages)
126
+ ## 4. P1 — Islands Architecture (Multi-Framework Pages)
127
127
 
128
128
  **What Astro does:**
129
129
  Pages are rendered as static HTML with interactive "islands" — individual components that hydrate independently. Each island can be a different framework (React, Svelte, Vue, etc.) on the same page. Hydration is controlled with directives: `client:load` (immediate), `client:idle` (requestIdleCallback), `client:visible` (IntersectionObserver). Non-interactive content ships zero JS.
@@ -412,112 +412,7 @@ The key insight: `defineIslandRegistry` accepts actual imported components at ru
412
412
 
413
413
  ---
414
414
 
415
- ## P2Sass/SCSS/Less Preprocessing
416
-
417
- **What Next.js does:**
418
- Built-in Sass support. Import `.scss` or `.sass` files directly. Also supports `.module.scss` for scoped Sass modules.
419
-
420
- **What AbsoluteJS has today:**
421
- Only `.css` files. Tailwind handles utility classes. No preprocessor support.
422
-
423
- **What needs to be built:**
424
- - A Bun build plugin that compiles `.scss`/`.sass`/`.less` files to CSS before bundling
425
- - Bun has a plugin API for custom loaders — register `.scss` extension with a loader that calls the sass compiler
426
- - Support `.module.scss` for scoped Sass modules (Bun's CSS Module handling + Sass compilation)
427
- - Add `sass` as an optional peer dependency
428
-
429
- **Files likely involved:**
430
- - New: `src/build/sassPlugin.ts` — Bun plugin that compiles Sass
431
- - `src/core/build.ts` — register the plugin in the Bun.build() calls
432
- - `src/build/scanCssEntryPoints.ts` — extend glob to include `**/*.scss`, `**/*.sass`, `**/*.less`
433
-
434
- ---
435
-
436
- ## P2 — Middleware
437
-
438
- **What Next.js does:**
439
- A single `middleware.ts` at the project root that runs before every request. Can rewrite URLs, redirect, set headers, check auth, do A/B testing, geolocation-based routing. Runs on the edge (lightweight V8 isolate).
440
-
441
- **What AbsoluteJS has today:**
442
- Elysia's full middleware system — `onBeforeHandle`, `onAfterHandle`, `.use()` plugin chain, `derive`, `guard`. This is actually more powerful than Next.js middleware but less conventionalized.
443
-
444
- **What needs to be built:**
445
- - Honestly, this might just need documentation. Elysia's `onBeforeHandle` IS middleware. A `guard()` block with auth checks IS the auth middleware pattern.
446
- - Consider a thin `middleware()` helper that wraps the common pattern: check auth, redirect if not authenticated, rewrite URLs, set CORS headers
447
- - Examples showing: auth guard, redirect, URL rewrite, rate limiting, CORS
448
- - The key gap isn't functionality — it's discoverability. New users don't know that Elysia's `onBeforeHandle` is the middleware they're looking for.
449
-
450
- **Files likely involved:**
451
- - Mostly documentation/examples
452
- - Optional: `src/utils/middleware.ts` — convenience wrappers for common patterns
453
-
454
- ---
455
-
456
- ## P3 — Internationalization (i18n)
457
-
458
- **What Next.js does:**
459
- Built-in locale routing (`/en/about`, `/fr/about`), locale detection from Accept-Language header, and domain-based routing. Integrates with i18n libraries like next-intl.
460
-
461
- **What AbsoluteJS has today:**
462
- Nothing.
463
-
464
- **What needs to be built:**
465
- - Locale detection middleware (Accept-Language header parsing, cookie-based locale persistence)
466
- - URL prefix routing pattern (`/:locale/page`)
467
- - A helper to load translation files and inject them as props
468
- - Per-framework translation access patterns (React context, Vue provide/inject, Svelte stores)
469
- - This is mostly a pattern/plugin, not core framework work
470
-
471
- **Files likely involved:**
472
- - New: `src/plugins/i18n.ts` — Elysia plugin for locale detection and routing
473
- - Documentation showing integration with popular i18n libraries
474
-
475
- ---
476
-
477
- ## P3 — Font Optimization
478
-
479
- **What Next.js does:**
480
- `next/font` automatically downloads Google Fonts at build time (no external requests), subsets them, adds `font-display: swap`, and inlines the CSS. Self-hosted fonts get the same optimizations. Zero layout shift from font loading.
481
-
482
- **What AbsoluteJS has today:**
483
- `generateHeadElement()` adds a Google Fonts `<link>` with `display=swap`. Fonts are loaded at runtime from Google's CDN.
484
-
485
- **What needs to be built:**
486
- - A build-time step that downloads declared Google Fonts, subsets them (woff2), and writes them to the assets directory
487
- - Inline the `@font-face` CSS directly in the `<head>` instead of linking to Google
488
- - This eliminates the external request to Google, improves privacy, and prevents FOUT
489
- - Consider a `defineFont()` helper that takes a font config and returns the CSS + paths
490
-
491
- **Files likely involved:**
492
- - New: `src/build/downloadFonts.ts` — fetches and subsets Google Fonts at build time
493
- - `src/utils/generateHeadElement.ts` — inline font CSS instead of external link
494
- - `src/core/build.ts` — add font download step to build pipeline
495
-
496
- ---
497
-
498
- ## P3 — Edge Runtime / Serverless Deployment
499
-
500
- **What Next.js does:**
501
- Routes can opt into the Edge Runtime (lightweight V8) for lower latency at the edge. Serverless function deployment on Vercel, AWS Lambda, Cloudflare Workers. Middleware always runs on edge.
502
-
503
- **What AbsoluteJS has today:**
504
- Bun-only. Requires a long-running Bun server process. No serverless or edge adapter.
505
-
506
- **What needs to be built:**
507
- - Deployment adapters for common platforms (Fly.io, Railway, Render are easy since they support Bun directly)
508
- - Docker template with a minimal Bun image
509
- - For serverless: an adapter that wraps the Elysia server as a Lambda/Cloud Function handler
510
- - Edge runtime is unlikely to be worth pursuing — Bun doesn't run on Cloudflare Workers, and the Bun server is already fast enough that edge latency gains are marginal
511
- - Focus on making traditional deployment dead simple rather than chasing edge
512
-
513
- **Files likely involved:**
514
- - New: `src/adapters/docker/Dockerfile`
515
- - New: `src/adapters/lambda.ts` — AWS Lambda adapter
516
- - Documentation for common deployment targets
517
-
518
- ---
519
-
520
- ## P1 — Out-of-Order Streaming
415
+ ## 5. P1 Out-of-Order Streaming
521
416
 
522
417
  **What SolidStart does:**
523
418
  Components stream to the client as they resolve, not in DOM order. If your sidebar data query finishes before your main content query, the sidebar HTML ships first. The browser renders each chunk into the correct DOM position regardless of arrival order. This means the fastest data always appears first — no waterfall where a slow hero section blocks the entire page.
@@ -573,7 +468,7 @@ With in-order streaming, the activity feed waits for the stats section even thou
573
468
 
574
469
  ---
575
470
 
576
- ## P1 — Form Actions with Progressive Enhancement
471
+ ## 6. P1 — Form Actions with Progressive Enhancement
577
472
 
578
473
  **What SvelteKit and Remix do:**
579
474
  Forms submit to the server as plain HTML `<form action="/submit" method="POST">` — this works with zero JavaScript. The server processes the form, validates input, and returns a result (redirect, error, or updated page). When JavaScript IS available, the framework intercepts the submission, sends it via `fetch()` instead, and updates the page without a full reload. The developer writes one handler that works both ways.
@@ -664,77 +559,7 @@ Elysia POST handlers work for form processing, but there's no convention for pro
664
559
 
665
560
  ---
666
561
 
667
- ## P2Partial Prerendering (requires SSG)
668
-
669
- **What Next.js 16 does:**
670
- A page is split into a static shell (navbar, footer, layout — cached at CDN) and dynamic "holes" that stream in at request time (user-specific content, real-time data). The static parts load instantly from cache while the dynamic parts stream in via SSR. From the user's perspective, the page appears instantly with personalized content filling in smoothly.
671
-
672
- Next.js implements this by combining static generation with Suspense boundaries — everything outside a `<Suspense>` boundary is pre-rendered at build time, and the Suspense fallbacks are replaced with streamed server-rendered content at request time.
673
-
674
- **What AbsoluteJS has today:**
675
- Every page is fully server-rendered at request time. No static caching of any page content. The entire page waits for all data before any HTML is sent (unless using streaming SSR, which streams in-order but still requires the server to render everything on each request).
676
-
677
- **Why this matters:**
678
- Most pages are 80% static content (nav, sidebar, footer, headings, layout) and 20% dynamic (user name, notifications, personalized feed). Rendering that 80% on every request is wasted work. Partial prerendering means:
679
- - The static shell loads from CDN in ~50ms (no server round-trip)
680
- - The dynamic holes stream from the server in ~200-500ms
681
- - Combined: users see a near-instant page with dynamic content appearing smoothly
682
- - Server load drops dramatically — most of the HTML is served from cache
683
-
684
- **What needs to be built (depends on SSG being implemented first):**
685
-
686
- *Build-time static shell generation:*
687
- - During the build, render each page but stop at dynamic boundaries (Suspense, `{#await}`, `<Suspense>`, `@defer`)
688
- - Write the static HTML (everything outside dynamic boundaries) to disk with placeholder slots for the dynamic parts
689
- - The static shell includes all CSS, the page layout, and fallback content (loading skeletons) for each dynamic slot
690
-
691
- *Request-time dynamic streaming:*
692
- - When a request comes in, serve the static shell immediately from disk/cache
693
- - Simultaneously, render the dynamic parts on the server and stream them into the placeholder slots
694
- - Reuse the out-of-order streaming infrastructure — the `$RC` swap script handles inserting dynamic content into the static shell
695
-
696
- *Per-framework dynamic boundaries:*
697
- - **React**: `<Suspense>` boundaries — everything inside is dynamic, everything outside is static
698
- - **Svelte**: `{#await}` blocks or a new `<Dynamic>` component
699
- - **Vue**: `<Suspense>` component — same pattern as React
700
- - **Angular**: `@defer` blocks — natural fit, Angular already distinguishes static vs deferred content
701
-
702
- *Caching strategy:*
703
- - Static shells cached in memory and/or on disk with content-hash keys
704
- - Cache invalidation: rebuild the shell when the page's static parts change (detected via file watcher or manual invalidation)
705
- - Dynamic parts are never cached (they're user-specific/time-specific)
706
- - CDN integration: set `Cache-Control` headers so the static shell is edge-cached while the dynamic stream bypasses cache
707
-
708
- *Configuration:*
709
- - Per-page opt-in via a config option or export:
710
- ```ts
711
- // In the route handler
712
- app.get('/dashboard', () =>
713
- handleReactPageRequest(Dashboard, manifest['DashboardIndex'], {
714
- prerender: 'partial', // static shell + dynamic streaming
715
- props: { userId: getCurrentUser() }
716
- })
717
- )
718
- ```
719
- - Or via the build config for pages that should always be partially prerendered
720
-
721
- **Design considerations:**
722
- - The static shell must be a valid HTML document on its own — if dynamic streaming fails, the user sees the shell with fallback content (loading skeletons), not a broken page.
723
- - Dynamic boundaries should be explicit — the developer marks what's dynamic, everything else is assumed static. No guessing.
724
- - This compounds with islands — an island with `hydrate="visible"` inside a dynamic boundary gets: static shell → streamed SSR HTML → hydrated on scroll. Three layers of progressive loading.
725
- - Hot reloading in dev: skip the static cache and render everything server-side (same as today). Partial prerendering is a production optimization only.
726
-
727
- **Files likely involved:**
728
- - Builds on top of SSG implementation and out-of-order streaming
729
- - New: `src/core/partialPrerender.ts` — orchestrates static shell serving + dynamic streaming
730
- - New: `src/build/generateStaticShells.ts` — renders pages at build time, extracts static content, writes shells to disk
731
- - `src/utils/streamingSlots.ts` — reused from out-of-order streaming for dynamic slot insertion
732
- - Each framework's `pageHandler.ts` — add `prerender: 'partial'` mode that serves cached shell + streams dynamic parts
733
- - `src/plugins/hmr.ts` — static shell cache invalidation when source files change
734
-
735
- ---
736
-
737
- ## P1 — AI/LLM Streaming Helpers
562
+ ## 7. P1 AI/LLM Streaming Helpers
738
563
 
739
564
  **What exists today in the ecosystem:**
740
565
  Vercel's `ai` SDK (`npm install ai`) provides React hooks and server utilities for streaming LLM responses. It works with Next.js, SvelteKit, and Nuxt. But it uses Server-Sent Events (SSE) because Next.js has no native WebSocket support. SSE is one-directional (server → client only) — the client can't send messages mid-stream (cancel, follow-up, branch conversation) without opening a new HTTP request.
@@ -967,7 +792,7 @@ type AIServerMessage =
967
792
 
968
793
  ---
969
794
 
970
- ## P1 — Type-Safe Environment Variables (`defineEnv`)
795
+ ## 8. P1 — Type-Safe Environment Variables (`defineEnv`)
971
796
 
972
797
  **The problem:**
973
798
  `process.env.DATABASE_URL` is always `string | undefined` in TypeScript. Typos are silent (`process.env.DATABSE_URL` → `undefined`, no error). Missing vars cause runtime crashes in production. Numeric vars like `PORT` come back as strings and need manual parsing. There's no single place to see what env vars an app requires.
@@ -1034,7 +859,7 @@ if (env.NODE_ENV === 'development') { ... } // narrowed union
1034
859
 
1035
860
  ---
1036
861
 
1037
- ## P1 — Security Headers + CSP Nonce Injection
862
+ ## 9. P1 — Security Headers + CSP Nonce Injection
1038
863
 
1039
864
  **The problem:**
1040
865
  Most web apps ship with zero security headers. Without a Content-Security-Policy, any XSS vulnerability lets attackers inject arbitrary `<script>` tags that execute freely. AbsoluteJS injects inline scripts on every page (`window.__INITIAL_PROPS__=...`, `$RefreshReg$` buffer, HMR client) — all of these are blocked by a strict CSP unless they have a per-request nonce.
@@ -1133,7 +958,118 @@ app.use(secureHeaders({
1133
958
 
1134
959
  ---
1135
960
 
1136
- ## P2 — Web Vitals Reporting
961
+ ## 10. P2 — Sass/SCSS/Less Preprocessing
962
+
963
+ **What Next.js does:**
964
+ Built-in Sass support. Import `.scss` or `.sass` files directly. Also supports `.module.scss` for scoped Sass modules.
965
+
966
+ **What AbsoluteJS has today:**
967
+ Only `.css` files. Tailwind handles utility classes. No preprocessor support.
968
+
969
+ **What needs to be built:**
970
+ - A Bun build plugin that compiles `.scss`/`.sass`/`.less` files to CSS before bundling
971
+ - Bun has a plugin API for custom loaders — register `.scss` extension with a loader that calls the sass compiler
972
+ - Support `.module.scss` for scoped Sass modules (Bun's CSS Module handling + Sass compilation)
973
+ - Add `sass` as an optional peer dependency
974
+
975
+ **Files likely involved:**
976
+ - New: `src/build/sassPlugin.ts` — Bun plugin that compiles Sass
977
+ - `src/core/build.ts` — register the plugin in the Bun.build() calls
978
+ - `src/build/scanCssEntryPoints.ts` — extend glob to include `**/*.scss`, `**/*.sass`, `**/*.less`
979
+
980
+ ---
981
+
982
+ ## 11. P2 — Middleware
983
+
984
+ **What Next.js does:**
985
+ A single `middleware.ts` at the project root that runs before every request. Can rewrite URLs, redirect, set headers, check auth, do A/B testing, geolocation-based routing. Runs on the edge (lightweight V8 isolate).
986
+
987
+ **What AbsoluteJS has today:**
988
+ Elysia's full middleware system — `onBeforeHandle`, `onAfterHandle`, `.use()` plugin chain, `derive`, `guard`. This is actually more powerful than Next.js middleware but less conventionalized.
989
+
990
+ **What needs to be built:**
991
+ - Honestly, this might just need documentation. Elysia's `onBeforeHandle` IS middleware. A `guard()` block with auth checks IS the auth middleware pattern.
992
+ - Consider a thin `middleware()` helper that wraps the common pattern: check auth, redirect if not authenticated, rewrite URLs, set CORS headers
993
+ - Examples showing: auth guard, redirect, URL rewrite, rate limiting, CORS
994
+ - The key gap isn't functionality — it's discoverability. New users don't know that Elysia's `onBeforeHandle` is the middleware they're looking for.
995
+
996
+ **Files likely involved:**
997
+ - Mostly documentation/examples
998
+ - Optional: `src/utils/middleware.ts` — convenience wrappers for common patterns
999
+
1000
+ ---
1001
+
1002
+ ## 12. P2 — Partial Prerendering (requires SSG)
1003
+
1004
+ **What Next.js 16 does:**
1005
+ A page is split into a static shell (navbar, footer, layout — cached at CDN) and dynamic "holes" that stream in at request time (user-specific content, real-time data). The static parts load instantly from cache while the dynamic parts stream in via SSR. From the user's perspective, the page appears instantly with personalized content filling in smoothly.
1006
+
1007
+ Next.js implements this by combining static generation with Suspense boundaries — everything outside a `<Suspense>` boundary is pre-rendered at build time, and the Suspense fallbacks are replaced with streamed server-rendered content at request time.
1008
+
1009
+ **What AbsoluteJS has today:**
1010
+ Every page is fully server-rendered at request time. No static caching of any page content. The entire page waits for all data before any HTML is sent (unless using streaming SSR, which streams in-order but still requires the server to render everything on each request).
1011
+
1012
+ **Why this matters:**
1013
+ Most pages are 80% static content (nav, sidebar, footer, headings, layout) and 20% dynamic (user name, notifications, personalized feed). Rendering that 80% on every request is wasted work. Partial prerendering means:
1014
+ - The static shell loads from CDN in ~50ms (no server round-trip)
1015
+ - The dynamic holes stream from the server in ~200-500ms
1016
+ - Combined: users see a near-instant page with dynamic content appearing smoothly
1017
+ - Server load drops dramatically — most of the HTML is served from cache
1018
+
1019
+ **What needs to be built (depends on SSG being implemented first):**
1020
+
1021
+ *Build-time static shell generation:*
1022
+ - During the build, render each page but stop at dynamic boundaries (Suspense, `{#await}`, `<Suspense>`, `@defer`)
1023
+ - Write the static HTML (everything outside dynamic boundaries) to disk with placeholder slots for the dynamic parts
1024
+ - The static shell includes all CSS, the page layout, and fallback content (loading skeletons) for each dynamic slot
1025
+
1026
+ *Request-time dynamic streaming:*
1027
+ - When a request comes in, serve the static shell immediately from disk/cache
1028
+ - Simultaneously, render the dynamic parts on the server and stream them into the placeholder slots
1029
+ - Reuse the out-of-order streaming infrastructure — the `$RC` swap script handles inserting dynamic content into the static shell
1030
+
1031
+ *Per-framework dynamic boundaries:*
1032
+ - **React**: `<Suspense>` boundaries — everything inside is dynamic, everything outside is static
1033
+ - **Svelte**: `{#await}` blocks or a new `<Dynamic>` component
1034
+ - **Vue**: `<Suspense>` component — same pattern as React
1035
+ - **Angular**: `@defer` blocks — natural fit, Angular already distinguishes static vs deferred content
1036
+
1037
+ *Caching strategy:*
1038
+ - Static shells cached in memory and/or on disk with content-hash keys
1039
+ - Cache invalidation: rebuild the shell when the page's static parts change (detected via file watcher or manual invalidation)
1040
+ - Dynamic parts are never cached (they're user-specific/time-specific)
1041
+ - CDN integration: set `Cache-Control` headers so the static shell is edge-cached while the dynamic stream bypasses cache
1042
+
1043
+ *Configuration:*
1044
+ - Per-page opt-in via a config option or export:
1045
+ ```ts
1046
+ // In the route handler
1047
+ app.get('/dashboard', () =>
1048
+ handleReactPageRequest(Dashboard, manifest['DashboardIndex'], {
1049
+ prerender: 'partial', // static shell + dynamic streaming
1050
+ props: { userId: getCurrentUser() }
1051
+ })
1052
+ )
1053
+ ```
1054
+ - Or via the build config for pages that should always be partially prerendered
1055
+
1056
+ **Design considerations:**
1057
+ - The static shell must be a valid HTML document on its own — if dynamic streaming fails, the user sees the shell with fallback content (loading skeletons), not a broken page.
1058
+ - Dynamic boundaries should be explicit — the developer marks what's dynamic, everything else is assumed static. No guessing.
1059
+ - This compounds with islands — an island with `hydrate="visible"` inside a dynamic boundary gets: static shell → streamed SSR HTML → hydrated on scroll. Three layers of progressive loading.
1060
+ - Hot reloading in dev: skip the static cache and render everything server-side (same as today). Partial prerendering is a production optimization only.
1061
+
1062
+ **Files likely involved:**
1063
+ - Builds on top of SSG implementation and out-of-order streaming
1064
+ - New: `src/core/partialPrerender.ts` — orchestrates static shell serving + dynamic streaming
1065
+ - New: `src/build/generateStaticShells.ts` — renders pages at build time, extracts static content, writes shells to disk
1066
+ - `src/utils/streamingSlots.ts` — reused from out-of-order streaming for dynamic slot insertion
1067
+ - Each framework's `pageHandler.ts` — add `prerender: 'partial'` mode that serves cached shell + streams dynamic parts
1068
+ - `src/plugins/hmr.ts` — static shell cache invalidation when source files change
1069
+
1070
+ ---
1071
+
1072
+ ## 13. P2 — Web Vitals Reporting
1137
1073
 
1138
1074
  **The problem:**
1139
1075
  43% of sites fail the INP (Interaction to Next Paint) threshold. Most developers don't measure real user performance until complaints come in. Adding analytics requires third-party scripts that themselves hurt performance. Vercel has `reportWebVitals()` for Next.js but no other meta-framework has this built in.
@@ -1208,7 +1144,7 @@ app.use(vitals({
1208
1144
 
1209
1145
  ---
1210
1146
 
1211
- ## P2 — Background Jobs + Cron
1147
+ ## 14. P2 — Background Jobs + Cron
1212
1148
 
1213
1149
  **The problem:**
1214
1150
  Web frameworks only handle request/response. Anything async — email sending, image processing, scheduled cleanups, webhook retries, report generation — requires separate infrastructure (Redis + Bull, separate worker process, cron service). This is the #1 reason apps "outgrow" their framework. Laravel and Rails have built-in queues and schedulers. No JS meta-framework has this.
@@ -1324,7 +1260,7 @@ app.use(jobs({
1324
1260
 
1325
1261
  ---
1326
1262
 
1327
- ## P2 — Health Check Endpoints
1263
+ ## 15. P2 — Health Check Endpoints
1328
1264
 
1329
1265
  **The problem:**
1330
1266
  Every Kubernetes, Docker, ECS, or Fly.io deployment needs health check endpoints. Without them, the orchestrator can't tell if your app is alive, ready to accept traffic, or stuck. Every team implements these ad-hoc with slightly different patterns.
@@ -1403,7 +1339,7 @@ app.use(healthChecks({
1403
1339
 
1404
1340
  ---
1405
1341
 
1406
- ## P2 — Structured Logging with Request Context
1342
+ ## 16. P2 — Structured Logging with Request Context
1407
1343
 
1408
1344
  **The problem:**
1409
1345
  `console.log` in production is useless — no request context, no correlation, no structured format. When something breaks, developers grep through unstructured text logs trying to match a request to its errors. Every log line should know which request it belongs to without the developer passing a logger through every function.
@@ -1484,7 +1420,7 @@ app.use(logging({
1484
1420
 
1485
1421
  ---
1486
1422
 
1487
- ## P2 — Parallel Data Loading
1423
+ ## 17. P2 — Parallel Data Loading
1488
1424
 
1489
1425
  **The problem:**
1490
1426
  Request waterfalls are the #1 hidden performance killer. A parent component fetches data, renders a child, which fetches its own data, creating sequential roundtrips. On a page with 3 data sources each taking 100ms, a waterfall takes 300ms while parallel loading takes 100ms.
@@ -1589,7 +1525,7 @@ app.get('/dashboard', async (ctx) => {
1589
1525
 
1590
1526
  ---
1591
1527
 
1592
- ## P2 — CLI Scaffolding / Page Generator
1528
+ ## 18. P2 — CLI Scaffolding / Page Generator
1593
1529
 
1594
1530
  **The problem:**
1595
1531
  Adding a new page to an AbsoluteJS app requires: creating the page component file with the right structure, creating a CSS file, adding the route to `server.ts` with the correct page handler import and manifest keys, and updating the build config if needed. This is 3-5 files and getting the imports/manifest keys wrong is a common mistake.
@@ -1688,7 +1624,7 @@ bun abs generate component Button --framework react
1688
1624
 
1689
1625
  ---
1690
1626
 
1691
- ## P2 — CLI Framework Adder
1627
+ ## 19. P2 — CLI Framework Adder
1692
1628
 
1693
1629
  **The problem:**
1694
1630
  A project starts with React only. Six months later the team wants to add a Svelte page for a performance-critical widget, or a Vue page because a new hire knows Vue. Today this requires manually creating the framework directory, installing dependencies, updating `absolute.config.ts`, adding the page handler import to `server.ts`, and knowing the correct handler API for that framework. It's error-prone and undocumented.
@@ -1807,3 +1743,67 @@ bun abs remove svelte
1807
1743
  - New: `src/cli/generators/addFramework.ts` — shared logic for directory creation, config updates, server.ts modification
1808
1744
  - Reuse generators from `create-absolutejs/src/generators/` — `scaffoldReact`, `scaffoldSvelte`, `scaffoldVue`, `scaffoldAngular`, `scaffoldHTML`, `scaffoldHTMX`
1809
1745
  - Reuse `generateImportsBlock` and `generateRoutesBlock` from `create-absolutejs/src/generators/project/`
1746
+
1747
+ ---
1748
+
1749
+ ## 20. P3 — Internationalization (i18n)
1750
+
1751
+ **What Next.js does:**
1752
+ Built-in locale routing (`/en/about`, `/fr/about`), locale detection from Accept-Language header, and domain-based routing. Integrates with i18n libraries like next-intl.
1753
+
1754
+ **What AbsoluteJS has today:**
1755
+ Nothing.
1756
+
1757
+ **What needs to be built:**
1758
+ - Locale detection middleware (Accept-Language header parsing, cookie-based locale persistence)
1759
+ - URL prefix routing pattern (`/:locale/page`)
1760
+ - A helper to load translation files and inject them as props
1761
+ - Per-framework translation access patterns (React context, Vue provide/inject, Svelte stores)
1762
+ - This is mostly a pattern/plugin, not core framework work
1763
+
1764
+ **Files likely involved:**
1765
+ - New: `src/plugins/i18n.ts` — Elysia plugin for locale detection and routing
1766
+ - Documentation showing integration with popular i18n libraries
1767
+
1768
+ ---
1769
+
1770
+ ## 21. P3 — Font Optimization
1771
+
1772
+ **What Next.js does:**
1773
+ `next/font` automatically downloads Google Fonts at build time (no external requests), subsets them, adds `font-display: swap`, and inlines the CSS. Self-hosted fonts get the same optimizations. Zero layout shift from font loading.
1774
+
1775
+ **What AbsoluteJS has today:**
1776
+ `generateHeadElement()` adds a Google Fonts `<link>` with `display=swap`. Fonts are loaded at runtime from Google's CDN.
1777
+
1778
+ **What needs to be built:**
1779
+ - A build-time step that downloads declared Google Fonts, subsets them (woff2), and writes them to the assets directory
1780
+ - Inline the `@font-face` CSS directly in the `<head>` instead of linking to Google
1781
+ - This eliminates the external request to Google, improves privacy, and prevents FOUT
1782
+ - Consider a `defineFont()` helper that takes a font config and returns the CSS + paths
1783
+
1784
+ **Files likely involved:**
1785
+ - New: `src/build/downloadFonts.ts` — fetches and subsets Google Fonts at build time
1786
+ - `src/utils/generateHeadElement.ts` — inline font CSS instead of external link
1787
+ - `src/core/build.ts` — add font download step to build pipeline
1788
+
1789
+ ---
1790
+
1791
+ ## 22. P3 — Edge Runtime / Serverless Deployment
1792
+
1793
+ **What Next.js does:**
1794
+ Routes can opt into the Edge Runtime (lightweight V8) for lower latency at the edge. Serverless function deployment on Vercel, AWS Lambda, Cloudflare Workers. Middleware always runs on edge.
1795
+
1796
+ **What AbsoluteJS has today:**
1797
+ Bun-only. Requires a long-running Bun server process. No serverless or edge adapter.
1798
+
1799
+ **What needs to be built:**
1800
+ - Deployment adapters for common platforms (Fly.io, Railway, Render are easy since they support Bun directly)
1801
+ - Docker template with a minimal Bun image
1802
+ - For serverless: an adapter that wraps the Elysia server as a Lambda/Cloud Function handler
1803
+ - Edge runtime is unlikely to be worth pursuing — Bun doesn't run on Cloudflare Workers, and the Bun server is already fast enough that edge latency gains are marginal
1804
+ - Focus on making traditional deployment dead simple rather than chasing edge
1805
+
1806
+ **Files likely involved:**
1807
+ - New: `src/adapters/docker/Dockerfile`
1808
+ - New: `src/adapters/lambda.ts` — AWS Lambda adapter
1809
+ - Documentation for common deployment targets
@@ -0,0 +1,140 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue';
3
+ import type { ImageProps } from '../../../types/image';
4
+ import {
5
+ DEFAULT_QUALITY,
6
+ buildOptimizedUrl,
7
+ generateBlurSvg,
8
+ generateSrcSet
9
+ } from '../../utils/imageProcessing';
10
+
11
+ const props = withDefaults(defineProps<ImageProps>(), {
12
+ quality: DEFAULT_QUALITY,
13
+ loading: 'lazy'
14
+ });
15
+
16
+ const blurRemoved = ref(false);
17
+
18
+ const resolvedSrc = computed(() => {
19
+ if (props.overrideSrc) return props.overrideSrc;
20
+ if (props.unoptimized) return props.src;
21
+ if (props.loader) return props.loader({ src: props.src, width: props.width ?? 0, quality: props.quality });
22
+ if (!props.width) return buildOptimizedUrl(props.src, 0, props.quality);
23
+ return buildOptimizedUrl(props.src, props.width, props.quality);
24
+ });
25
+
26
+ const srcSet = computed(() =>
27
+ props.unoptimized
28
+ ? undefined
29
+ : generateSrcSet(props.src, props.width, props.sizes, undefined, props.loader ?? undefined)
30
+ );
31
+
32
+ const resolvedSizes = computed(() => props.sizes ?? (props.fill ? '100vw' : undefined));
33
+
34
+ const resolvedLoading = computed(() => (props.priority ? 'eager' : props.loading));
35
+
36
+ const resolvedFetchPriority = computed(() => (props.priority ? 'high' : props.fetchPriority));
37
+
38
+ const hasBlur = computed(() =>
39
+ props.placeholder === 'blur' ||
40
+ (typeof props.placeholder === 'string' &&
41
+ props.placeholder !== 'empty' &&
42
+ props.placeholder.startsWith('data:'))
43
+ );
44
+
45
+ const blurBackground = computed(() => {
46
+ if (!hasBlur.value || blurRemoved.value) return undefined;
47
+ if (
48
+ typeof props.placeholder === 'string' &&
49
+ props.placeholder !== 'blur' &&
50
+ props.placeholder.startsWith('data:')
51
+ ) {
52
+ return generateBlurSvg(props.placeholder);
53
+ }
54
+ if (props.blurDataURL) return generateBlurSvg(props.blurDataURL);
55
+ return undefined;
56
+ });
57
+
58
+ const imgStyle = computed(() => {
59
+ const base: Record<string, string | number> = {
60
+ ...(props.style ?? {}),
61
+ color: 'transparent'
62
+ };
63
+ if (blurBackground.value) {
64
+ base.backgroundImage = blurBackground.value;
65
+ base.backgroundSize = 'cover';
66
+ base.backgroundPosition = 'center';
67
+ base.backgroundRepeat = 'no-repeat';
68
+ }
69
+ if (props.fill) {
70
+ base.position = 'absolute';
71
+ base.height = '100%';
72
+ base.width = '100%';
73
+ base.inset = '0';
74
+ base.objectFit = 'cover';
75
+ }
76
+ return base;
77
+ });
78
+
79
+ const handleLoad = (e: Event) => {
80
+ blurRemoved.value = true;
81
+ if (props.onLoad) (props.onLoad as (event: Event) => void)(e);
82
+ };
83
+
84
+ const handleError = (e: Event) => {
85
+ if (props.onError) (props.onError as (event: Event) => void)(e);
86
+ };
87
+ </script>
88
+
89
+ <template>
90
+ <Teleport to="head" v-if="priority">
91
+ <link
92
+ rel="preload"
93
+ as="image"
94
+ :href="resolvedSrc"
95
+ :imagesrcset="srcSet"
96
+ :imagesizes="resolvedSizes"
97
+ :crossorigin="crossOrigin"
98
+ />
99
+ </Teleport>
100
+
101
+ <span
102
+ v-if="fill"
103
+ style="position: relative; overflow: hidden; display: block; width: 100%; height: 100%"
104
+ >
105
+ <img
106
+ :alt="alt"
107
+ :src="resolvedSrc"
108
+ :srcset="srcSet"
109
+ :sizes="resolvedSizes"
110
+ :loading="resolvedLoading"
111
+ :class="className"
112
+ :style="imgStyle"
113
+ :crossorigin="crossOrigin"
114
+ :referrerpolicy="referrerPolicy"
115
+ :fetchpriority="resolvedFetchPriority"
116
+ decoding="async"
117
+ @load="handleLoad"
118
+ @error="handleError"
119
+ />
120
+ </span>
121
+
122
+ <img
123
+ v-else
124
+ :alt="alt"
125
+ :src="resolvedSrc"
126
+ :srcset="srcSet"
127
+ :sizes="resolvedSizes"
128
+ :width="width"
129
+ :height="height"
130
+ :loading="resolvedLoading"
131
+ :class="className"
132
+ :style="imgStyle"
133
+ :crossorigin="crossOrigin"
134
+ :referrerpolicy="referrerPolicy"
135
+ :fetchpriority="resolvedFetchPriority"
136
+ decoding="async"
137
+ @load="handleLoad"
138
+ @error="handleError"
139
+ />
140
+ </template>
@@ -0,0 +1,33 @@
1
+ import type { ImageLoader } from '../../../types/image';
2
+ export declare class ImageComponent {
3
+ readonly alt: import("@angular/core").InputSignal<string>;
4
+ readonly blurDataURL: import("@angular/core").InputSignal<string | undefined>;
5
+ readonly className: import("@angular/core").InputSignal<string | undefined>;
6
+ readonly crossOrigin: import("@angular/core").InputSignal<"" | "anonymous" | "use-credentials" | undefined>;
7
+ readonly fetchPriority: import("@angular/core").InputSignal<"high" | "low" | "auto" | undefined>;
8
+ readonly fill: import("@angular/core").InputSignal<boolean>;
9
+ readonly height: import("@angular/core").InputSignal<number | undefined>;
10
+ readonly loader: import("@angular/core").InputSignal<ImageLoader | undefined>;
11
+ readonly loading: import("@angular/core").InputSignal<"lazy" | "eager">;
12
+ readonly onError: import("@angular/core").InputSignal<((event: Event) => void) | undefined>;
13
+ readonly onLoad: import("@angular/core").InputSignal<((event: Event) => void) | undefined>;
14
+ readonly overrideSrc: import("@angular/core").InputSignal<string | undefined>;
15
+ readonly placeholder: import("@angular/core").InputSignal<string>;
16
+ readonly priority: import("@angular/core").InputSignal<boolean>;
17
+ readonly quality: import("@angular/core").InputSignal<number>;
18
+ readonly referrerPolicy: import("@angular/core").InputSignal<string | undefined>;
19
+ readonly sizes: import("@angular/core").InputSignal<string | undefined>;
20
+ readonly src: import("@angular/core").InputSignal<string>;
21
+ readonly style: import("@angular/core").InputSignal<Record<string, string | number> | undefined>;
22
+ readonly unoptimized: import("@angular/core").InputSignal<boolean>;
23
+ readonly width: import("@angular/core").InputSignal<number | undefined>;
24
+ private readonly blurRemoved;
25
+ readonly resolvedSrc: import("@angular/core").Signal<string>;
26
+ readonly srcSet: import("@angular/core").Signal<string | undefined>;
27
+ readonly resolvedSizes: import("@angular/core").Signal<string | undefined>;
28
+ readonly resolvedLoading: import("@angular/core").Signal<"lazy" | "eager">;
29
+ readonly resolvedFetchPriority: import("@angular/core").Signal<"high" | "low" | "auto" | undefined>;
30
+ readonly imgStyle: import("@angular/core").Signal<Record<string, string | number>>;
31
+ handleLoad(event: Event): void;
32
+ handleError(event: Event): void;
33
+ }
@@ -0,0 +1 @@
1
+ export { ImageComponent } from './image.component';