@stone-js/starters 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/basic-react-declarative/app/Application.tsx +22 -7
- package/basic-react-declarative/assets/css/index.css +163 -18
- package/basic-react-declarative/package.json +8 -5
- package/basic-react-declarative/tests/Application.spec.ts +2 -2
- package/basic-react-imperative/app/Application.tsx +22 -7
- package/basic-react-imperative/assets/css/index.css +163 -18
- package/basic-react-imperative/package.json +8 -5
- package/basic-react-imperative/tests/Application.spec.ts +4 -2
- package/basic-service-declarative/app/Application.ts +18 -3
- package/basic-service-declarative/package.json +7 -4
- package/basic-service-declarative/tests/Application.spec.ts +3 -2
- package/basic-service-imperative/app/Application.ts +18 -3
- package/basic-service-imperative/package.json +7 -4
- package/basic-service-imperative/tests/Application.spec.ts +3 -2
- package/continuum-showcase/app/layouts/MainLayout.tsx +13 -7
- package/continuum-showcase/app/pages/BlogPostPage.tsx +5 -2
- package/continuum-showcase/app/pages/HomePage.tsx +19 -6
- package/continuum-showcase/assets/css/index.css +190 -18
- package/continuum-showcase/package.json +14 -8
- package/continuum-showcase/tests/HomePage.spec.tsx +4 -3
- package/full-react-declarative/app/middleware/AuthMiddleware.ts +1 -1
- package/full-react-declarative/app/pages/home/HomePage.tsx +22 -21
- package/full-react-declarative/assets/css/index.css +167 -0
- package/full-react-declarative/package.json +13 -10
- package/full-react-declarative/tests/HomePage.spec.ts +38 -0
- package/full-react-imperative/app/middleware/AuthMiddleware.ts +1 -1
- package/full-react-imperative/app/pages/home/HomePage.tsx +32 -16
- package/full-react-imperative/assets/css/index.css +167 -0
- package/full-react-imperative/package.json +13 -10
- package/full-react-imperative/tests/HomePage.spec.ts +45 -0
- package/full-service-declarative/app/handlers/WelcomeEventHandler.ts +47 -0
- package/full-service-declarative/app/middleware/AuthMiddleware.ts +1 -1
- package/full-service-declarative/package.json +13 -10
- package/full-service-declarative/tests/WelcomeEventHandler.spec.ts +54 -0
- package/full-service-imperative/app/handlers/WelcomeEventHandler.ts +51 -0
- package/full-service-imperative/app/middleware/AuthMiddleware.ts +1 -1
- package/full-service-imperative/package.json +13 -10
- package/full-service-imperative/tests/WelcomeEventHandler.spec.ts +31 -0
- package/package.json +6 -7
- package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +37 -5
- package/standard-react-declarative/assets/css/index.css +163 -18
- package/standard-react-declarative/package.json +11 -8
- package/standard-react-declarative/tests/WelcomePage.spec.ts +2 -0
- package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +37 -5
- package/standard-react-imperative/assets/css/index.css +163 -18
- package/standard-react-imperative/package.json +11 -8
- package/standard-react-imperative/tests/WelcomePage.spec.ts +2 -0
- package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +2 -2
- package/standard-service-declarative/app/services/WelcomeService.ts +28 -2
- package/standard-service-declarative/package.json +9 -6
- package/standard-service-declarative/tests/Application.spec.ts +13 -4
- package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +2 -4
- package/standard-service-imperative/app/services/welcomeService.ts +28 -2
- package/standard-service-imperative/package.json +9 -6
- package/standard-service-imperative/tests/Application.spec.ts +12 -3
|
@@ -8,13 +8,22 @@ import { defineStoneApp, defineConfig, IBlueprint, ILogger, IncomingEvent, Funct
|
|
|
8
8
|
export const FactoryHandler = ({ logger }: AppOptions): FunctionalEventHandler<IncomingEvent, ResponseData> => {
|
|
9
9
|
return (event: IncomingEvent): ResponseData => {
|
|
10
10
|
// Get the name from the event
|
|
11
|
-
const
|
|
11
|
+
const name = String(event.get<string>('name', 'World'))
|
|
12
|
+
const message = `Hello ${name}! Welcome to Stone.js.`
|
|
12
13
|
|
|
13
14
|
// Log a message
|
|
14
15
|
logger.info(message)
|
|
15
16
|
|
|
16
|
-
// Return a JSON response
|
|
17
|
-
return {
|
|
17
|
+
// Return a branded JSON response
|
|
18
|
+
return {
|
|
19
|
+
message,
|
|
20
|
+
framework: {
|
|
21
|
+
name: 'Stone.js',
|
|
22
|
+
tagline: 'The continuum framework',
|
|
23
|
+
docs: 'https://stonejs.dev',
|
|
24
|
+
github: 'https://github.com/stone-foundation/stone-js-framework'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
}
|
|
20
29
|
|
|
@@ -57,4 +66,10 @@ interface AppOptions {
|
|
|
57
66
|
*/
|
|
58
67
|
export interface ResponseData {
|
|
59
68
|
message: string
|
|
69
|
+
framework: {
|
|
70
|
+
name: string
|
|
71
|
+
tagline: string
|
|
72
|
+
docs: string
|
|
73
|
+
github: string
|
|
74
|
+
}
|
|
60
75
|
}
|
|
@@ -18,17 +18,20 @@
|
|
|
18
18
|
"test:cvg": "npm run test -- --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/core": "^0.8.
|
|
22
|
-
"@stone-js/node-cli-adapter": "^0.8.
|
|
23
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
21
|
+
"@stone-js/core": "^0.8.6",
|
|
22
|
+
"@stone-js/node-cli-adapter": "^0.8.6",
|
|
23
|
+
"@stone-js/node-http-adapter": "^0.8.6"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
27
27
|
"@babel/preset-env": "^7.26.9",
|
|
28
28
|
"@babel/preset-typescript": "^7.27.0",
|
|
29
|
-
"@stone-js/cli": "^0.8.
|
|
29
|
+
"@stone-js/cli": "^0.8.6",
|
|
30
30
|
"@types/node": "^24.0.7",
|
|
31
31
|
"@vitest/coverage-v8": "^3.0.9",
|
|
32
32
|
"vitest": "^3.0.9"
|
|
33
|
+
},
|
|
34
|
+
"overrides": {
|
|
35
|
+
"glob": "^13.0.0"
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -34,8 +34,9 @@ describe('Application', () => {
|
|
|
34
34
|
// Act
|
|
35
35
|
const response = await app(event)
|
|
36
36
|
// Assert
|
|
37
|
-
expect(response.message).toBe('Hello World!')
|
|
38
|
-
expect(
|
|
37
|
+
expect(response.message).toBe('Hello World! Welcome to Stone.js.')
|
|
38
|
+
expect(response.framework.name).toBe('Stone.js')
|
|
39
|
+
expect(mockedLogger.info).toHaveBeenCalledWith('Hello World! Welcome to Stone.js.')
|
|
39
40
|
})
|
|
40
41
|
|
|
41
42
|
it('should get config', () => {
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { PageLayout, IPageLayout, PageLayoutRenderContext } from '@stone-js/use-react'
|
|
2
|
+
import { PageLayout, StoneOutlet, IPageLayout, PageLayoutRenderContext } from '@stone-js/use-react'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* The default layout
|
|
6
|
-
*
|
|
5
|
+
* The default layout: a thin branded chrome (top bar + footer) around the page outlet.
|
|
6
|
+
* The page content is placed via <StoneOutlet>, never a bare {children}.
|
|
7
7
|
*/
|
|
8
8
|
@PageLayout({ name: 'default' })
|
|
9
9
|
export class MainLayout implements IPageLayout {
|
|
10
10
|
render ({ children }: PageLayoutRenderContext) {
|
|
11
11
|
return (
|
|
12
|
-
<div className=
|
|
13
|
-
<header
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
<div className='cx-shell'>
|
|
13
|
+
<header className='cx-nav'>
|
|
14
|
+
<a className='cx-wordmark' href='/'><span className='dot'>●</span> Stone.js</a>
|
|
15
|
+
<nav>
|
|
16
|
+
<a href='/'>Home</a>
|
|
17
|
+
<a href='/blog/hello-world'>Blog</a>
|
|
18
|
+
</nav>
|
|
19
|
+
</header>
|
|
20
|
+
<StoneOutlet>{children}</StoneOutlet>
|
|
21
|
+
<footer className='cx-foot'>Built with Stone.js — the continuum framework</footer>
|
|
16
22
|
</div>
|
|
17
23
|
)
|
|
18
24
|
}
|
|
@@ -20,7 +20,7 @@ interface Post {
|
|
|
20
20
|
* Blog post page — demonstrates a fully dynamic, data-driven head built from the
|
|
21
21
|
* loader (`handle`) output, including an `article` Open Graph type and per-post JSON-LD.
|
|
22
22
|
*/
|
|
23
|
-
@Page(
|
|
23
|
+
@Page('/blog/:slug')
|
|
24
24
|
export class BlogPostPage implements IPage<ReactIncomingEvent> {
|
|
25
25
|
handle (event: ReactIncomingEvent): Post {
|
|
26
26
|
const slug = event.get<string>('slug', 'hello-world')
|
|
@@ -53,8 +53,11 @@ export class BlogPostPage implements IPage<ReactIncomingEvent> {
|
|
|
53
53
|
|
|
54
54
|
render ({ data }: PageRenderContext<Post>) {
|
|
55
55
|
return (
|
|
56
|
-
<article>
|
|
56
|
+
<article className='cx-article'>
|
|
57
|
+
<a className='cx-back' href='/'>← Back</a>
|
|
58
|
+
<p className='cx-kicker'>Stone.js Blog</p>
|
|
57
59
|
<h1>{data?.title}</h1>
|
|
60
|
+
<p className='cx-excerpt'>{data?.excerpt}</p>
|
|
58
61
|
<p>{data?.body}</p>
|
|
59
62
|
</article>
|
|
60
63
|
)
|
|
@@ -18,7 +18,7 @@ interface HomeData {
|
|
|
18
18
|
* Twitter card and JSON-LD structured data), all from `head()`;
|
|
19
19
|
* - importing a static asset via the `@img` alias (resolved by the CLI's Vite config).
|
|
20
20
|
*/
|
|
21
|
-
@Page(
|
|
21
|
+
@Page('/')
|
|
22
22
|
export class HomePage implements IPage<ReactIncomingEvent> {
|
|
23
23
|
handle (): HomeData {
|
|
24
24
|
return { message: 'Write your domain once. Stone.js applies the context.' }
|
|
@@ -48,11 +48,24 @@ export class HomePage implements IPage<ReactIncomingEvent> {
|
|
|
48
48
|
|
|
49
49
|
render ({ data }: PageRenderContext<HomeData>) {
|
|
50
50
|
return (
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
<section className='stone-welcome'>
|
|
52
|
+
<div className='glow' aria-hidden='true' />
|
|
53
|
+
<div className='hero'>
|
|
54
|
+
<img className='mark' src={logo} alt='Stone.js' width={104} height={104} />
|
|
55
|
+
<p className='eyebrow'>The continuum showcase</p>
|
|
56
|
+
<h1 className='title'>Stone.js</h1>
|
|
57
|
+
<p className='lead'>{data?.message}</p>
|
|
58
|
+
<p className='tagline'>
|
|
59
|
+
One domain, rendered on the server and hydrated in the browser, with a fluent
|
|
60
|
+
head / OpenGraph API and static-asset aliases.
|
|
61
|
+
</p>
|
|
62
|
+
<nav className='links'>
|
|
63
|
+
<a href='/blog/hello-world'>Read the blog demo</a>
|
|
64
|
+
<a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
|
|
65
|
+
<span className='edit'>Edit <b>app/pages/HomePage.tsx</b></span>
|
|
66
|
+
</nav>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
56
69
|
)
|
|
57
70
|
}
|
|
58
71
|
}
|
|
@@ -1,26 +1,198 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Stone.js starter — brand welcome screen.
|
|
3
|
+
* Identity: "Obsidienne & Braise" — obsidian ground, ember (braise) accent, the Portal mark.
|
|
4
|
+
* Theme-aware: dark by default, light under prefers-color-scheme: light.
|
|
5
|
+
*/
|
|
6
|
+
:root {
|
|
7
|
+
--ink: #14100E;
|
|
8
|
+
--ink-2: #1E1815;
|
|
9
|
+
--cream: #F4EFE6;
|
|
10
|
+
--cream-dim: #B8AE9E;
|
|
11
|
+
--ember-1: #FFC46B;
|
|
12
|
+
--ember-2: #FF5A1F;
|
|
13
|
+
--line: rgba(244, 239, 230, 0.10);
|
|
14
|
+
--card: rgba(244, 239, 230, 0.04);
|
|
15
|
+
--bg: radial-gradient(1200px 600px at 50% -10%, #241C17 0%, var(--ink) 55%);
|
|
9
16
|
}
|
|
10
|
-
|
|
17
|
+
|
|
18
|
+
@media (prefers-color-scheme: light) {
|
|
19
|
+
:root {
|
|
20
|
+
--ink: #F4EFE6;
|
|
21
|
+
--ink-2: #ffffff;
|
|
22
|
+
--cream: #1B1613;
|
|
23
|
+
--cream-dim: #6B6154;
|
|
24
|
+
--line: rgba(27, 22, 19, 0.10);
|
|
25
|
+
--card: rgba(27, 22, 19, 0.03);
|
|
26
|
+
--bg: radial-gradient(1200px 600px at 50% -10%, #FFFFFF 0%, #F1E9DC 60%);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
* { box-sizing: border-box; }
|
|
31
|
+
|
|
32
|
+
body {
|
|
11
33
|
margin: 0;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
34
|
+
min-height: 100vh;
|
|
35
|
+
color: var(--cream);
|
|
36
|
+
background: var(--ink);
|
|
37
|
+
font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
38
|
+
-webkit-font-smoothing: antialiased;
|
|
15
39
|
}
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
|
|
41
|
+
.stone-welcome {
|
|
42
|
+
position: relative;
|
|
43
|
+
min-height: 100vh;
|
|
18
44
|
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
align-items: center;
|
|
19
47
|
justify-content: center;
|
|
48
|
+
padding: 48px 24px;
|
|
49
|
+
background: var(--bg);
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Ember glow behind the mark. */
|
|
54
|
+
.stone-welcome .glow {
|
|
55
|
+
position: absolute;
|
|
56
|
+
top: 8%;
|
|
57
|
+
left: 50%;
|
|
58
|
+
width: 520px;
|
|
59
|
+
height: 520px;
|
|
60
|
+
transform: translateX(-50%);
|
|
61
|
+
background: radial-gradient(circle, rgba(255, 90, 31, 0.22) 0%, rgba(255, 90, 31, 0) 68%);
|
|
62
|
+
filter: blur(8px);
|
|
63
|
+
pointer-events: none;
|
|
64
|
+
z-index: 0;
|
|
20
65
|
}
|
|
21
|
-
|
|
66
|
+
|
|
67
|
+
.stone-welcome .hero {
|
|
68
|
+
position: relative;
|
|
69
|
+
z-index: 1;
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-width: 680px;
|
|
22
72
|
text-align: center;
|
|
73
|
+
animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.stone-welcome .mark {
|
|
77
|
+
width: 104px;
|
|
78
|
+
height: 104px;
|
|
79
|
+
filter: drop-shadow(0 8px 28px rgba(255, 90, 31, 0.35));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.stone-welcome .eyebrow {
|
|
83
|
+
margin: 22px 0 0;
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
letter-spacing: 0.28em;
|
|
87
|
+
text-transform: uppercase;
|
|
88
|
+
color: var(--cream-dim);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.stone-welcome .title {
|
|
92
|
+
margin: 8px 0 0;
|
|
93
|
+
font-size: clamp(44px, 8vw, 76px);
|
|
94
|
+
font-weight: 800;
|
|
95
|
+
letter-spacing: -0.03em;
|
|
96
|
+
line-height: 1.02;
|
|
97
|
+
background: linear-gradient(92deg, var(--ember-1), var(--ember-2));
|
|
98
|
+
-webkit-background-clip: text;
|
|
99
|
+
background-clip: text;
|
|
100
|
+
color: transparent;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.stone-welcome .lead {
|
|
104
|
+
margin: 18px auto 0;
|
|
105
|
+
font-size: 20px;
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
color: var(--cream);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.stone-welcome .tagline {
|
|
111
|
+
margin: 10px auto 0;
|
|
112
|
+
max-width: 46ch;
|
|
113
|
+
font-size: 16px;
|
|
114
|
+
line-height: 1.6;
|
|
115
|
+
color: var(--cream-dim);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.stone-welcome .links {
|
|
119
|
+
margin-top: 34px;
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-wrap: wrap;
|
|
122
|
+
gap: 12px;
|
|
123
|
+
justify-content: center;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.stone-welcome .links a,
|
|
127
|
+
.stone-welcome .links .edit {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 8px;
|
|
131
|
+
padding: 11px 18px;
|
|
132
|
+
border: 1px solid var(--line);
|
|
133
|
+
border-radius: 12px;
|
|
134
|
+
background: var(--card);
|
|
135
|
+
color: var(--cream);
|
|
136
|
+
font-size: 14px;
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
text-decoration: none;
|
|
139
|
+
transition: border-color 0.2s, transform 0.2s, background 0.2s;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.stone-welcome .links a:hover {
|
|
143
|
+
transform: translateY(-2px);
|
|
144
|
+
border-color: rgba(255, 90, 31, 0.6);
|
|
145
|
+
background: rgba(255, 90, 31, 0.08);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.stone-welcome .links .edit {
|
|
149
|
+
font-family: ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, monospace;
|
|
150
|
+
color: var(--cream-dim);
|
|
151
|
+
}
|
|
152
|
+
.stone-welcome .links .edit b { color: var(--ember-1); font-weight: 700; }
|
|
153
|
+
|
|
154
|
+
.stone-welcome .brand {
|
|
155
|
+
position: relative;
|
|
156
|
+
z-index: 1;
|
|
157
|
+
margin-top: 56px;
|
|
158
|
+
font-size: 13px;
|
|
159
|
+
letter-spacing: 0.02em;
|
|
160
|
+
color: var(--cream-dim);
|
|
161
|
+
}
|
|
162
|
+
.stone-welcome .brand .dot { color: var(--ember-2); }
|
|
163
|
+
|
|
164
|
+
@keyframes rise {
|
|
165
|
+
from { opacity: 0; transform: translateY(14px); }
|
|
166
|
+
to { opacity: 1; transform: translateY(0); }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@media (prefers-reduced-motion: reduce) {
|
|
170
|
+
.stone-welcome .hero { animation: none; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* --- Showcase chrome (MainLayout) --- */
|
|
174
|
+
.cx-shell { min-height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
|
|
175
|
+
.cx-nav {
|
|
176
|
+
position: sticky; top: 0; z-index: 5;
|
|
177
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
178
|
+
padding: 16px 24px; border-bottom: 1px solid var(--line);
|
|
179
|
+
backdrop-filter: blur(8px);
|
|
23
180
|
}
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
}
|
|
181
|
+
.cx-wordmark { font-weight: 800; letter-spacing: -0.02em; color: var(--cream); text-decoration: none; }
|
|
182
|
+
.cx-wordmark .dot { color: var(--ember-2); }
|
|
183
|
+
.cx-nav nav { display: flex; gap: 18px; }
|
|
184
|
+
.cx-nav nav a { color: var(--cream-dim); text-decoration: none; font-size: 14px; font-weight: 600; }
|
|
185
|
+
.cx-nav nav a:hover { color: var(--ember-1); }
|
|
186
|
+
.cx-foot { padding: 22px; text-align: center; color: var(--cream-dim); font-size: 13px; border-top: 1px solid var(--line); }
|
|
187
|
+
|
|
188
|
+
/* The hero fills the space between nav and footer rather than a full extra viewport. */
|
|
189
|
+
.cx-shell .stone-welcome { min-height: calc(100vh - 140px); }
|
|
190
|
+
|
|
191
|
+
/* --- Article (BlogPostPage) --- */
|
|
192
|
+
.cx-article { max-width: 720px; margin: 0 auto; padding: 64px 24px; color: var(--cream); }
|
|
193
|
+
.cx-back { color: var(--cream-dim); text-decoration: none; font-size: 14px; font-weight: 600; }
|
|
194
|
+
.cx-back:hover { color: var(--ember-1); }
|
|
195
|
+
.cx-kicker { margin: 28px 0 0; font-size: 12px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--ember-2); font-weight: 700; }
|
|
196
|
+
.cx-article h1 { margin: 8px 0 0; font-size: clamp(32px, 5vw, 48px); letter-spacing: -0.02em; }
|
|
197
|
+
.cx-excerpt { margin-top: 12px; font-size: 19px; color: var(--cream-dim); line-height: 1.6; }
|
|
198
|
+
.cx-article > p:last-child { margin-top: 20px; font-size: 16px; line-height: 1.75; }
|
|
@@ -15,23 +15,29 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"react": "^19.0.0",
|
|
17
17
|
"react-dom": "^19.0.0",
|
|
18
|
-
"@stone-js/core": "^0.8.
|
|
19
|
-
"@stone-js/router": "^0.8.
|
|
20
|
-
"@stone-js/use-react": "^0.8.
|
|
21
|
-
"@stone-js/http-core": "^0.8.
|
|
22
|
-
"@stone-js/pipeline": "^0.8.
|
|
23
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
24
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
18
|
+
"@stone-js/core": "^0.8.6",
|
|
19
|
+
"@stone-js/router": "^0.8.6",
|
|
20
|
+
"@stone-js/use-react": "^0.8.6",
|
|
21
|
+
"@stone-js/http-core": "^0.8.6",
|
|
22
|
+
"@stone-js/pipeline": "^0.8.6",
|
|
23
|
+
"@stone-js/browser-adapter": "^0.8.6",
|
|
24
|
+
"@stone-js/node-http-adapter": "^0.8.6"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
28
28
|
"@babel/preset-env": "^7.26.9",
|
|
29
29
|
"@babel/preset-typescript": "^7.27.0",
|
|
30
|
-
"@stone-js/cli": "^0.8.
|
|
30
|
+
"@stone-js/cli": "^0.8.6",
|
|
31
31
|
"@types/node": "^24.0.7",
|
|
32
32
|
"@types/react": "^19.0.7",
|
|
33
33
|
"@types/react-dom": "^19.0.3",
|
|
34
34
|
"@vitest/coverage-v8": "^3.0.9",
|
|
35
35
|
"vitest": "^3.0.9"
|
|
36
|
+
},
|
|
37
|
+
"overrides": {
|
|
38
|
+
"glob": "^13.0.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20.0.0"
|
|
36
42
|
}
|
|
37
43
|
}
|
|
@@ -29,12 +29,13 @@ describe('HomePage', () => {
|
|
|
29
29
|
// Act
|
|
30
30
|
const html = renderToString(page.render({ data: page.handle() } as any))
|
|
31
31
|
|
|
32
|
-
// Assert: the Stone.js logo
|
|
33
|
-
//
|
|
32
|
+
// Assert: the premium welcome hero renders the Stone.js logo (Vite inlines the small SVG
|
|
33
|
+
// as a data URI), the brand title and the domain message.
|
|
34
|
+
expect(html).toContain('stone-welcome')
|
|
34
35
|
expect(html).toContain('<img')
|
|
35
36
|
expect(html).toContain('image/svg+xml')
|
|
36
37
|
expect(html).toContain('alt="Stone.js"')
|
|
37
|
-
expect(html).toContain('Stone.js
|
|
38
|
+
expect(html).toContain('Stone.js')
|
|
38
39
|
expect(html).toContain('Write your domain once')
|
|
39
40
|
})
|
|
40
41
|
})
|
|
@@ -18,7 +18,7 @@ export interface AuthMiddlewareOptions {
|
|
|
18
18
|
*/
|
|
19
19
|
@Middleware({
|
|
20
20
|
global: true,
|
|
21
|
-
params: [['/login', '/register']]
|
|
21
|
+
params: [['/', '/login', '/register']]
|
|
22
22
|
})
|
|
23
23
|
export class AuthMiddleware implements IMiddleware<ReactIncomingEvent, ReactOutgoingResponse> {
|
|
24
24
|
private readonly snapshot: ISnapshot
|
|
@@ -8,8 +8,8 @@ import { IPage, Page, ReactIncomingEvent, StoneLink, HeadContext } from '@stone-
|
|
|
8
8
|
export class HomePage implements IPage<ReactIncomingEvent> {
|
|
9
9
|
head (): HeadContext {
|
|
10
10
|
return {
|
|
11
|
-
title: '
|
|
12
|
-
description: '
|
|
11
|
+
title: 'Welcome to Stone.js',
|
|
12
|
+
description: 'Stone.js Blog Dashboard — write your domain once, Stone.js is the context that runs it anywhere.'
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -20,25 +20,26 @@ export class HomePage implements IPage<ReactIncomingEvent> {
|
|
|
20
20
|
*/
|
|
21
21
|
render (): JSX.Element {
|
|
22
22
|
return (
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</
|
|
41
|
-
|
|
23
|
+
<main className='stone-welcome'>
|
|
24
|
+
<div className='glow' aria-hidden='true' />
|
|
25
|
+
<section className='hero'>
|
|
26
|
+
<img className='mark' src='/logo.svg' alt='Stone.js' width={104} height={104} />
|
|
27
|
+
<p className='eyebrow'>Welcome to</p>
|
|
28
|
+
<h1 className='title'>Stone.js</h1>
|
|
29
|
+
<p className='lead'>Your Blog Dashboard is running.</p>
|
|
30
|
+
<p className='tagline'>
|
|
31
|
+
Write your domain once, Stone.js is the context that runs it anywhere:
|
|
32
|
+
server, serverless, browser, CLI and the edge.
|
|
33
|
+
</p>
|
|
34
|
+
<nav className='links'>
|
|
35
|
+
<StoneLink to='/posts'>Browse Posts</StoneLink>
|
|
36
|
+
<StoneLink to='/users'>Browse Users</StoneLink>
|
|
37
|
+
<a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
|
|
38
|
+
<a href='https://github.com/stone-foundation/stone-js-framework' target='_blank' rel='noreferrer noopener'>GitHub</a>
|
|
39
|
+
</nav>
|
|
40
|
+
</section>
|
|
41
|
+
<footer className='brand'><span className='dot'>●</span> Stone.js — the continuum framework</footer>
|
|
42
|
+
</main>
|
|
42
43
|
)
|
|
43
44
|
}
|
|
44
45
|
}
|