@stone-js/starters 0.8.8 → 0.8.10

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 (101) hide show
  1. package/README.md +2 -2
  2. package/basic-react-declarative/app/Application.tsx +0 -2
  3. package/basic-react-declarative/package.json +7 -7
  4. package/basic-react-declarative/public/favicon.svg +16 -0
  5. package/basic-react-declarative/tests/Application.spec.ts +25 -100
  6. package/basic-react-imperative/app/Application.tsx +2 -17
  7. package/basic-react-imperative/package.json +7 -7
  8. package/basic-react-imperative/public/favicon.svg +16 -0
  9. package/basic-react-imperative/tests/Application.spec.ts +25 -86
  10. package/basic-react-native-declarative/App.tsx +192 -0
  11. package/basic-react-native-declarative/LICENSE +21 -0
  12. package/basic-react-native-declarative/README.md +35 -0
  13. package/basic-react-native-declarative/adapter/NativeAdapter.ts +108 -0
  14. package/basic-react-native-declarative/adapter/NativeErrorHandler.ts +53 -0
  15. package/basic-react-native-declarative/adapter/NativeEventSource.ts +58 -0
  16. package/basic-react-native-declarative/adapter/RawResponseWrapper.ts +30 -0
  17. package/basic-react-native-declarative/adapter/declarations.ts +40 -0
  18. package/basic-react-native-declarative/adapter/middleware.ts +73 -0
  19. package/basic-react-native-declarative/adapter/nativeAdapterBlueprint.ts +51 -0
  20. package/basic-react-native-declarative/adapter/renderSink.ts +52 -0
  21. package/basic-react-native-declarative/app/Application.ts +17 -0
  22. package/basic-react-native-declarative/app/WelcomeController.ts +66 -0
  23. package/basic-react-native-declarative/app.json +25 -0
  24. package/basic-react-native-declarative/assets/android-icon-background.png +0 -0
  25. package/basic-react-native-declarative/assets/android-icon-foreground.png +0 -0
  26. package/basic-react-native-declarative/assets/android-icon-monochrome.png +0 -0
  27. package/basic-react-native-declarative/assets/favicon.png +0 -0
  28. package/basic-react-native-declarative/assets/icon.png +0 -0
  29. package/basic-react-native-declarative/assets/splash-icon.png +0 -0
  30. package/basic-react-native-declarative/babel.config.js +16 -0
  31. package/basic-react-native-declarative/index.ts +22 -0
  32. package/basic-react-native-declarative/metro.config.js +7 -0
  33. package/basic-react-native-declarative/package.json +41 -0
  34. package/basic-react-native-declarative/tests/Application.spec.ts +65 -0
  35. package/basic-react-native-declarative/tsconfig.json +16 -0
  36. package/basic-react-native-declarative/vitest.config.ts +27 -0
  37. package/basic-service-declarative/app/Application.ts +0 -2
  38. package/basic-service-declarative/package.json +6 -6
  39. package/basic-service-declarative/tests/Application.spec.ts +20 -53
  40. package/basic-service-imperative/app/Application.ts +2 -14
  41. package/basic-service-imperative/package.json +6 -6
  42. package/basic-service-imperative/tests/Application.spec.ts +19 -44
  43. package/continuum-showcase/package.json +11 -10
  44. package/continuum-showcase/public/favicon.svg +16 -0
  45. package/continuum-showcase/stone.config.mjs +2 -2
  46. package/continuum-showcase/tests/application.spec.ts +30 -0
  47. package/full-react-declarative/app/Application.ts +0 -4
  48. package/full-react-declarative/package.json +12 -12
  49. package/full-react-declarative/public/favicon.svg +16 -0
  50. package/full-react-declarative/stone.config.mjs +2 -2
  51. package/full-react-declarative/tests/application.spec.ts +30 -0
  52. package/full-react-imperative/app/Application.ts +1 -3
  53. package/full-react-imperative/package.json +12 -12
  54. package/full-react-imperative/public/favicon.svg +16 -0
  55. package/full-react-imperative/stone.config.mjs +2 -2
  56. package/full-react-imperative/tests/application.spec.ts +30 -0
  57. package/full-service-declarative/app/Application.ts +5 -2
  58. package/full-service-declarative/app/configurations/AppConfiguration.ts +1 -3
  59. package/full-service-declarative/package.json +13 -12
  60. package/full-service-declarative/stone.config.mjs +2 -2
  61. package/full-service-declarative/tests/application.spec.ts +30 -0
  62. package/full-service-imperative/app/Application.ts +8 -2
  63. package/full-service-imperative/app/configurations/AppConfiguration.ts +1 -3
  64. package/full-service-imperative/package.json +13 -12
  65. package/full-service-imperative/stone.config.mjs +2 -2
  66. package/full-service-imperative/tests/application.spec.ts +30 -0
  67. package/package.json +15 -1
  68. package/standard-react-declarative/package.json +11 -10
  69. package/standard-react-declarative/public/favicon.svg +16 -0
  70. package/standard-react-declarative/tests/application.spec.ts +30 -0
  71. package/standard-react-imperative/package.json +11 -10
  72. package/standard-react-imperative/public/favicon.svg +16 -0
  73. package/standard-react-imperative/tests/application.spec.ts +30 -0
  74. package/standard-service-declarative/package.json +9 -8
  75. package/standard-service-declarative/tests/Application.spec.ts +22 -94
  76. package/standard-service-imperative/package.json +9 -8
  77. package/standard-service-imperative/tests/Application.spec.ts +22 -78
  78. package/basic-react-declarative/vitest.config.ts +0 -24
  79. package/basic-react-imperative/vitest.config.ts +0 -24
  80. package/basic-service-declarative/vitest.config.ts +0 -24
  81. package/basic-service-imperative/vitest.config.ts +0 -24
  82. package/continuum-showcase/tests/HomePage.spec.tsx +0 -41
  83. package/continuum-showcase/vitest.config.ts +0 -31
  84. package/full-react-declarative/tests/AppLayout.spec.ts +0 -51
  85. package/full-react-declarative/tests/HomePage.spec.ts +0 -38
  86. package/full-react-declarative/vitest.config.ts +0 -24
  87. package/full-react-imperative/tests/HomePage.spec.ts +0 -45
  88. package/full-react-imperative/tests/LayoutHeader.spec.ts +0 -39
  89. package/full-react-imperative/vitest.config.ts +0 -24
  90. package/full-service-declarative/tests/PostService.spec.ts +0 -56
  91. package/full-service-declarative/tests/WelcomeEventHandler.spec.ts +0 -54
  92. package/full-service-declarative/vitest.config.ts +0 -24
  93. package/full-service-imperative/tests/PostService.spec.ts +0 -47
  94. package/full-service-imperative/tests/WelcomeEventHandler.spec.ts +0 -31
  95. package/full-service-imperative/vitest.config.ts +0 -24
  96. package/standard-react-declarative/tests/WelcomePage.spec.ts +0 -71
  97. package/standard-react-declarative/vitest.config.ts +0 -24
  98. package/standard-react-imperative/tests/WelcomePage.spec.ts +0 -53
  99. package/standard-react-imperative/vitest.config.ts +0 -24
  100. package/standard-service-declarative/vitest.config.ts +0 -24
  101. package/standard-service-imperative/vitest.config.ts +0 -24
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "basic-react-native-declarative",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "description": "Stone.js's basic starter to create a React Native (Expo) app using the declarative API.",
6
+ "author": "Mr. Stone <evensstone@gmail.com>",
7
+ "license": "MIT",
8
+ "main": "index.ts",
9
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
12
+ "scripts": {
13
+ "start": "expo start",
14
+ "android": "expo start --android",
15
+ "ios": "expo start --ios",
16
+ "web": "expo start --web",
17
+ "export": "expo export",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "vitest run",
20
+ "test:cvg": "npm run test -- --coverage"
21
+ },
22
+ "dependencies": {
23
+ "@stone-js/browser-core": "^0.8.10",
24
+ "@stone-js/core": "^0.8.10",
25
+ "@stone-js/router": "^0.8.10",
26
+ "expo": "~57.0.14",
27
+ "expo-status-bar": "~57.0.1",
28
+ "fast-text-encoding": "^1.0.6",
29
+ "react": "19.2.3",
30
+ "react-native": "0.86.2",
31
+ "react-native-url-polyfill": "^3.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@babel/plugin-proposal-decorators": "^7.28.0",
35
+ "@types/react": "~19.2.2",
36
+ "@vitest/coverage-v8": "^3.0.9",
37
+ "babel-preset-expo": "^57.0.7",
38
+ "typescript": "~6.0.3",
39
+ "vitest": "^3.0.9"
40
+ }
41
+ }
@@ -0,0 +1,65 @@
1
+ import { stoneApp } from '@stone-js/core'
2
+ import { describe, it, expect, beforeAll } from 'vitest'
3
+ import { Application } from '../app/Application'
4
+ import { OutgoingBrowserResponse } from '@stone-js/browser-core'
5
+ import { nativeEventSource } from '../adapter/NativeEventSource'
6
+ import { WelcomeData, WelcomeController } from '../app/WelcomeController'
7
+ import { onNativeError, onNativeRender } from '../adapter/renderSink'
8
+
9
+ /**
10
+ * Behavioral test of the full continuum chain: the exact modules the native
11
+ * application boots (domain + router + native adapter) are booted here under
12
+ * Node, navigation intents are emitted, and the rendered payloads asserted.
13
+ *
14
+ * The adapter is pure JavaScript, so what passes here is what runs on device;
15
+ * only the platform checks (URL polyfill, Hermes) need the real application.
16
+ *
17
+ * The application boots ONCE for the whole suite, like on a device: default
18
+ * blueprints are shared module-level objects, so booting the same modules
19
+ * twice in one process would accumulate route definitions.
20
+ */
21
+ describe('Stone.js native proof of concept', () => {
22
+ const rendered: OutgoingBrowserResponse[] = []
23
+ const errors: Error[] = []
24
+
25
+ beforeAll(async () => {
26
+ onNativeRender((response) => rendered.push(response))
27
+ onNativeError((error) => errors.push(error))
28
+ await stoneApp({ modules: [Application, WelcomeController] }).run()
29
+ })
30
+
31
+ it('boots and renders the landing route on startup', () => {
32
+ expect(errors).toHaveLength(0)
33
+ expect(rendered).toHaveLength(1)
34
+
35
+ const payload = rendered[0].content as WelcomeData
36
+ expect(payload.route).toBe('/')
37
+ expect(payload.message).toContain('Welcome to Stone.js on React Native!')
38
+ expect(payload.framework.name).toBe('Stone.js')
39
+ })
40
+
41
+ it('routes a navigation intent with a parameter to the same domain', async () => {
42
+ nativeEventSource.navigate('stone://app/hello/Noowow')
43
+ await new Promise((resolve) => setImmediate(resolve))
44
+
45
+ const payload = rendered.at(-1)?.content as WelcomeData
46
+ expect(payload.route).toBe('/hello/Noowow')
47
+ expect(payload.message).toContain('Hello Noowow!')
48
+ })
49
+
50
+ it('decodes encoded route parameters', async () => {
51
+ nativeEventSource.navigate('stone://app/hello/Mr.%20Stone')
52
+ await new Promise((resolve) => setImmediate(resolve))
53
+
54
+ const payload = rendered.at(-1)?.content as WelcomeData
55
+ expect(payload.message).toContain('Hello Mr. Stone!')
56
+ })
57
+
58
+ it('surfaces unknown routes through the error chain instead of crashing', async () => {
59
+ nativeEventSource.navigate('stone://app/nowhere')
60
+ await new Promise((resolve) => setImmediate(resolve))
61
+
62
+ const lastPayload = rendered.at(-1)?.content as WelcomeData | undefined
63
+ expect(lastPayload?.route === '/nowhere').toBe(false)
64
+ })
65
+ })
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "experimentalDecorators": true,
6
+ "types": ["vitest/globals"]
7
+ },
8
+ "include": [
9
+ "app",
10
+ "adapter",
11
+ "tests",
12
+ "App.tsx",
13
+ "index.ts",
14
+ "vitest.config.ts"
15
+ ]
16
+ }
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ // `experimentalDecorators` is enabled in tsconfig.json for tsc TYPING only
5
+ // (the framework types its decorators with the legacy signatures). At
6
+ // RUNTIME Stone.js requires standard 2023-11 decorators, so esbuild must
7
+ // ignore that flag and emit the standard semantics, `Symbol.metadata`
8
+ // included (esbuild 0.25+ implements them correctly on its own).
9
+ esbuild: {
10
+ tsconfigRaw: {
11
+ compilerOptions: {
12
+ experimentalDecorators: false
13
+ }
14
+ }
15
+ },
16
+ test: {
17
+ globals: true,
18
+ environment: 'node',
19
+ include: ['./tests/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
20
+ coverage: {
21
+ provider: 'v8',
22
+ include: ['app/**/*.ts', 'adapter/**/*.ts'],
23
+ reporter: ['text', 'html'],
24
+ reportsDirectory: './coverage'
25
+ }
26
+ }
27
+ })
@@ -1,5 +1,4 @@
1
1
  import { NodeHttp } from '@stone-js/node-http-adapter'
2
- import { NodeConsole } from '@stone-js/node-cli-adapter'
3
2
  import { IncomingEvent, IEventHandler, ILogger, LogLevel, StoneApp } from '@stone-js/core'
4
3
 
5
4
  /**
@@ -10,7 +9,6 @@ import { IncomingEvent, IEventHandler, ILogger, LogLevel, StoneApp } from '@ston
10
9
  * @NodeHttp() is used to enable the Node HTTP adapter.
11
10
  * @StoneApp() is used to enable the Stone application, it is required.
12
11
  */
13
- @NodeConsole()
14
12
  @NodeHttp({ default: true })
15
13
  @StoneApp({ logger: { level: LogLevel.INFO } })
16
14
  export class Application implements IEventHandler<IncomingEvent> {
@@ -14,19 +14,19 @@
14
14
  "build": "stone build",
15
15
  "preview": "stone preview dist/server.mjs",
16
16
  "typings": "stone typings",
17
- "test": "vitest run",
18
- "test:cvg": "npm run test -- --coverage"
17
+ "test": "stone test",
18
+ "test:cvg": "stone test --coverage"
19
19
  },
20
20
  "dependencies": {
21
- "@stone-js/core": "^0.8.8",
22
- "@stone-js/node-cli-adapter": "^0.8.8",
23
- "@stone-js/node-http-adapter": "^0.8.8"
21
+ "@stone-js/core": "^0.8.10",
22
+ "@stone-js/node-http-adapter": "^0.8.10"
24
23
  },
25
24
  "devDependencies": {
26
25
  "@babel/plugin-proposal-decorators": "^7.25.9",
27
26
  "@babel/preset-env": "^7.26.9",
28
27
  "@babel/preset-typescript": "^7.27.0",
29
- "@stone-js/cli": "^0.8.8",
28
+ "@stone-js/cli": "^0.8.10",
29
+ "@stone-js/testing": "^0.8.10",
30
30
  "@types/node": "^24.0.7",
31
31
  "@vitest/coverage-v8": "^3.0.9",
32
32
  "vitest": "^3.0.9"
@@ -1,61 +1,28 @@
1
- import { Application } from '../app/Application'
2
- import { ILogger, IncomingEvent } from '@stone-js/core'
3
-
4
- // We must mock decorators to lighten the test environment
5
- vi.mock(import("@stone-js/core"), async (importOriginal) => {
6
- const actual = await importOriginal()
7
- return {
8
- ...actual,
9
- StoneApp: vi.fn(() => vi.fn()),
10
- }
11
- })
12
-
13
- // We must mock decorators to lighten the test environment
14
- vi.mock(import("@stone-js/node-cli-adapter"), async (importOriginal) => {
15
- const actual = await importOriginal()
16
- return {
17
- ...actual,
18
- NodeConsole: vi.fn(() => vi.fn()),
19
- }
20
- })
21
-
22
- // We must mock decorators to lighten the test environment
23
- vi.mock(import("@stone-js/node-http-adapter"), async (importOriginal) => {
24
- const actual = await importOriginal()
25
- return {
26
- ...actual,
27
- NodeHttp: vi.fn(() => vi.fn()),
28
- }
29
- })
30
-
1
+ import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
2
+
3
+ /**
4
+ * The whole application, booted in memory and asked a real question.
5
+ *
6
+ * Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
7
+ * through the same kernel production runs. If this passes the application works; if it fails the
8
+ * application is broken. That is the only kind of test worth shipping in a starter — the previous one
9
+ * stubbed out the framework's own decorators, which meant it could pass while nothing worked.
10
+ */
31
11
  describe('Application', () => {
32
- let app: Application
33
- let mockedLogger: ILogger
12
+ it('answers the caller', async () => {
13
+ const app = await createTestApp()
34
14
 
35
- beforeEach(() => {
36
- mockedLogger = {
37
- info: vi.fn(),
38
- } as unknown as ILogger
15
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
39
16
 
40
- app = new Application({ logger: mockedLogger })
17
+ expect(response.statusCode).toBe(200)
18
+ expect(response.json()).toMatchObject({ message: expect.stringContaining('Ada') })
41
19
  })
42
20
 
43
- it('should create an application instance', () => {
44
- // Assert
45
- expect(app).toBeInstanceOf(Application)
46
- })
21
+ it('answers when nobody says who they are', async () => {
22
+ const app = await createTestApp()
47
23
 
48
- it('should handle incoming events', () => {
49
- // Arrange
50
- const expectedMessage = 'Hello World! Welcome to Stone.js.'
51
- const event = { get: () => 'World' } as unknown as IncomingEvent
24
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
52
25
 
53
- // Act
54
- const response = app.handle(event)
55
-
56
- // Assert
57
- expect(response.message).toBe(expectedMessage)
58
- expect(response.framework.name).toBe('Stone.js')
59
- expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
26
+ expect(response.statusCode).toBe(200)
60
27
  })
61
- })
28
+ })
@@ -1,6 +1,5 @@
1
1
  import { NODE_HTTP_PLATFORM, nodeHttpAdapterBlueprint } from '@stone-js/node-http-adapter'
2
- import { defineCommand, NODE_CONSOLE_PLATFORM, nodeConsoleAdapterBlueprint } from '@stone-js/node-cli-adapter'
3
- import { defineStoneApp, defineConfig, IBlueprint, ILogger, IncomingEvent, FunctionalEventHandler } from '@stone-js/core'
2
+ import { defineStoneApp, ILogger, IncomingEvent, FunctionalEventHandler } from '@stone-js/core'
4
3
 
5
4
  /**
6
5
  * Create an handler using the factory handler.
@@ -40,20 +39,9 @@ export const Application = defineStoneApp(
40
39
  isFactory: true,
41
40
  adapter: { platform: NODE_HTTP_PLATFORM }
42
41
  },
43
- [nodeHttpAdapterBlueprint, nodeConsoleAdapterBlueprint]
42
+ [nodeHttpAdapterBlueprint]
44
43
  )
45
44
 
46
- /**
47
- * Application configuration.
48
- */
49
- export const AppConfig = defineConfig({
50
- afterConfigure (blueprint: IBlueprint) {
51
- if (blueprint.is('stone.adapter.platform', NODE_CONSOLE_PLATFORM)) {
52
- blueprint.set(defineCommand(FactoryHandler, { name: '*', isFactory: true }))
53
- }
54
- }
55
- })
56
-
57
45
  /**
58
46
  * Application options.
59
47
  */
@@ -14,19 +14,19 @@
14
14
  "build": "stone build",
15
15
  "preview": "stone preview dist/server.mjs",
16
16
  "typings": "stone typings",
17
- "test": "vitest run",
18
- "test:cvg": "npm run test -- --coverage"
17
+ "test": "stone test",
18
+ "test:cvg": "stone test --coverage"
19
19
  },
20
20
  "dependencies": {
21
- "@stone-js/core": "^0.8.8",
22
- "@stone-js/node-cli-adapter": "^0.8.8",
23
- "@stone-js/node-http-adapter": "^0.8.8"
21
+ "@stone-js/core": "^0.8.10",
22
+ "@stone-js/node-http-adapter": "^0.8.10"
24
23
  },
25
24
  "devDependencies": {
26
25
  "@babel/plugin-proposal-decorators": "^7.25.9",
27
26
  "@babel/preset-env": "^7.26.9",
28
27
  "@babel/preset-typescript": "^7.27.0",
29
- "@stone-js/cli": "^0.8.8",
28
+ "@stone-js/cli": "^0.8.10",
29
+ "@stone-js/testing": "^0.8.10",
30
30
  "@types/node": "^24.0.7",
31
31
  "@vitest/coverage-v8": "^3.0.9",
32
32
  "vitest": "^3.0.9"
@@ -1,53 +1,28 @@
1
- import { IncomingHttpEvent } from '@stone-js/http-core'
2
- import { FunctionalEventHandler, ILogger } from '@stone-js/core'
3
- import { AppConfig, FactoryHandler, ResponseData } from '../app/Application'
4
-
5
- vi.mock('@stone-js/core', async (mod) => {
6
- const actual: any = await mod()
7
- return {
8
- ...actual,
9
- isNotEmpty: vi.fn(() => true),
10
- defineConfig: (config: any) => config
11
- }
12
- })
13
-
1
+ import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
2
+
3
+ /**
4
+ * The whole application, booted in memory and asked a real question.
5
+ *
6
+ * Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
7
+ * through the same kernel production runs. If this passes the application works; if it fails the
8
+ * application is broken. That is the only kind of test worth shipping in a starter — the previous one
9
+ * stubbed out the framework's own decorators, which meant it could pass while nothing worked.
10
+ */
14
11
  describe('Application', () => {
15
- let mockedLogger: ILogger
16
- let app: FunctionalEventHandler<IncomingHttpEvent, ResponseData>
17
-
18
- beforeEach(() => {
19
- mockedLogger = {
20
- info: vi.fn(),
21
- } as unknown as ILogger
12
+ it('answers the caller', async () => {
13
+ const app = await createTestApp()
22
14
 
23
- app = FactoryHandler({ logger: mockedLogger })
24
- })
25
-
26
- it('should create an event handler instance', () => {
27
- // Assert
28
- expect(app).toBeTypeOf('function')
29
- })
15
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
30
16
 
31
- it('should handle incoming http events', async () => {
32
- // Arrange
33
- const event = { get: () => 'World' } as unknown as IncomingHttpEvent
34
- // Act
35
- const response = await app(event)
36
- // Assert
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.')
17
+ expect(response.statusCode).toBe(200)
18
+ expect(response.json()).toMatchObject({ message: expect.stringContaining('Ada') })
40
19
  })
41
20
 
42
- it('should get config', () => {
43
- // Arrange
44
- const config: any = AppConfig
45
- const blueprint: any = { is: () => true, set: vi.fn() }
21
+ it('answers when nobody says who they are', async () => {
22
+ const app = await createTestApp()
46
23
 
47
- // Act
48
- config.afterConfigure(blueprint)
24
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
49
25
 
50
- // Assert
51
- expect(blueprint.set).toHaveBeenCalled()
26
+ expect(response.statusCode).toBe(200)
52
27
  })
53
28
  })
@@ -9,25 +9,26 @@
9
9
  "build": "stone build",
10
10
  "build:ssr": "stone build --ssr",
11
11
  "preview": "stone preview",
12
- "test": "vitest run",
13
- "test:cvg": "npm run test -- --coverage"
12
+ "test": "stone test",
13
+ "test:cvg": "stone test --coverage"
14
14
  },
15
15
  "dependencies": {
16
16
  "react": "^19.0.0",
17
17
  "react-dom": "^19.0.0",
18
- "@stone-js/core": "^0.8.8",
19
- "@stone-js/router": "^0.8.8",
20
- "@stone-js/use-react": "^0.8.8",
21
- "@stone-js/http-core": "^0.8.8",
22
- "@stone-js/pipeline": "^0.8.8",
23
- "@stone-js/browser-adapter": "^0.8.8",
24
- "@stone-js/node-http-adapter": "^0.8.8"
18
+ "@stone-js/core": "^0.8.10",
19
+ "@stone-js/router": "^0.8.10",
20
+ "@stone-js/use-react": "^0.8.10",
21
+ "@stone-js/http-core": "^0.8.10",
22
+ "@stone-js/pipeline": "^0.8.10",
23
+ "@stone-js/browser-adapter": "^0.8.10",
24
+ "@stone-js/node-http-adapter": "^0.8.10"
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.8",
30
+ "@stone-js/cli": "^0.8.10",
31
+ "@stone-js/testing": "^0.8.10",
31
32
  "@types/node": "^24.0.7",
32
33
  "@types/react": "^19.0.7",
33
34
  "@types/react-dom": "^19.0.3",
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
2
+ <title>Stone.js</title>
3
+ <style>
4
+ .ink { stroke: #1B1613; }
5
+ @media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
6
+ </style>
7
+ <defs>
8
+ <linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
9
+ <stop offset="0" stop-color="#FFC46B" />
10
+ <stop offset="1" stop-color="#FF5A1F" />
11
+ </linearGradient>
12
+ </defs>
13
+ <path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
14
+ <path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
15
+ <path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
16
+ </svg>
@@ -1,4 +1,4 @@
1
- import { defineConfig } from '@stone-js/cli'
1
+ import { defineBuilderConfig } from '@stone-js/cli'
2
2
 
3
3
  /**
4
4
  * Build configuration for the showcase.
@@ -6,7 +6,7 @@ import { defineConfig } from '@stone-js/cli'
6
6
  * - `target: 'react'` + `rendering: 'ssr'` exercises the server-rendered head pipeline.
7
7
  * - `assets` enables the `@img` / `@css` / `@assets` import aliases used in components.
8
8
  */
9
- export default defineConfig({
9
+ export default defineBuilderConfig({
10
10
  target: 'react',
11
11
  rendering: 'ssr',
12
12
  assets: {
@@ -0,0 +1,30 @@
1
+ import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
2
+
3
+ /**
4
+ * The whole application, booted in memory and asked a real question.
5
+ *
6
+ * Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
7
+ * through the same kernel production runs. If this passes the application works; if it fails the
8
+ * application is broken. That is the only kind of test worth shipping in a starter — the previous one
9
+ * stubbed out the framework's own decorators, which meant it could pass while nothing worked.
10
+ */
11
+ describe('Pages', () => {
12
+ it('renders the home page, data included', async () => {
13
+ const app = await createTestApp()
14
+
15
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
16
+
17
+ expect(response.statusCode).toBe(200)
18
+ // A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
19
+ // whatever you already use (happy-dom, jsdom, Testing Library): none is bundled here.
20
+ expect(response.html()).toContain('Ada')
21
+ })
22
+
23
+ it('renders without a name', async () => {
24
+ const app = await createTestApp()
25
+
26
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
27
+
28
+ expect(response.statusCode).toBe(200)
29
+ })
30
+ })
@@ -2,7 +2,6 @@ import { Routing } from '@stone-js/router'
2
2
  import { Browser } from '@stone-js/browser-adapter'
3
3
  import { Hook, UseReact } from '@stone-js/use-react'
4
4
  import { NodeHttp } from '@stone-js/node-http-adapter'
5
- import { NodeConsole } from '@stone-js/node-cli-adapter'
6
5
  import { IBlueprint, IContainer, ILogger, LogLevel, Promiseable, StoneApp } from '@stone-js/core'
7
6
 
8
7
  /**
@@ -13,8 +12,6 @@ import { IBlueprint, IContainer, ILogger, LogLevel, Promiseable, StoneApp } from
13
12
  * @StoneApp() is used to enable the Stone application, it is mandatory.
14
13
  * @Routing() is used to enable the routing feature.
15
14
  * @NodeHttp() is used to enable the Node HTTP adapter.
16
- * @NodeConsole() is used to enable the Node Console adapter.
17
- * @NodeConsole() requires the incoming event type.
18
15
  * BodyEventMiddleware is used to parse the incoming event body.
19
16
  * BodyEventMiddleware is imported because it is not installed by default.
20
17
  *
@@ -25,7 +22,6 @@ import { IBlueprint, IContainer, ILogger, LogLevel, Promiseable, StoneApp } from
25
22
  @Routing()
26
23
  @Browser()
27
24
  @UseReact()
28
- @NodeConsole()
29
25
  @NodeHttp({ default: true, url: 'http://localhost:3100' })
30
26
  @StoneApp({ name: 'My Stone', logger: { level: LogLevel.DEBUG } })
31
27
  export class Application {
@@ -14,19 +14,18 @@
14
14
  "build": "stone build",
15
15
  "preview": "stone preview",
16
16
  "build:ssr": "stone build --ssr",
17
- "test": "vitest run",
18
- "test:cvg": "npm run test -- --coverage"
17
+ "test": "stone test",
18
+ "test:cvg": "stone test --coverage"
19
19
  },
20
20
  "dependencies": {
21
- "@stone-js/env": "^0.8.8",
22
- "@stone-js/http-core": "^0.8.8",
23
- "@stone-js/node-http-adapter": "^0.8.8",
24
- "@stone-js/router": "^0.8.8",
25
- "@stone-js/filesystem": "^0.8.8",
26
- "@stone-js/core": "^0.8.8",
27
- "@stone-js/use-react": "^0.8.8",
28
- "@stone-js/browser-adapter": "^0.8.8",
29
- "@stone-js/node-cli-adapter": "^0.8.8",
21
+ "@stone-js/env": "^0.8.10",
22
+ "@stone-js/http-core": "^0.8.10",
23
+ "@stone-js/node-http-adapter": "^0.8.10",
24
+ "@stone-js/router": "^0.8.10",
25
+ "@stone-js/filesystem": "^0.8.10",
26
+ "@stone-js/core": "^0.8.10",
27
+ "@stone-js/use-react": "^0.8.10",
28
+ "@stone-js/browser-adapter": "^0.8.10",
30
29
  "axios": "^1.7.9",
31
30
  "react": "^19.0.0",
32
31
  "react-dom": "^19.0.0"
@@ -35,7 +34,8 @@
35
34
  "@babel/plugin-proposal-decorators": "^7.25.9",
36
35
  "@babel/preset-env": "^7.26.9",
37
36
  "@babel/preset-typescript": "^7.27.0",
38
- "@stone-js/cli": "^0.8.8",
37
+ "@stone-js/cli": "^0.8.10",
38
+ "@stone-js/testing": "^0.8.10",
39
39
  "@types/axios": "^0.14.4",
40
40
  "@types/node": "^24.0.7",
41
41
  "@types/react": "^19.0.7",
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
2
+ <title>Stone.js</title>
3
+ <style>
4
+ .ink { stroke: #1B1613; }
5
+ @media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
6
+ </style>
7
+ <defs>
8
+ <linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
9
+ <stop offset="0" stop-color="#FFC46B" />
10
+ <stop offset="1" stop-color="#FF5A1F" />
11
+ </linearGradient>
12
+ </defs>
13
+ <path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
14
+ <path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
15
+ <path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
16
+ </svg>
@@ -1,9 +1,9 @@
1
- import { defineConfig } from '@stone-js/cli'
1
+ import { defineBuilderConfig } from '@stone-js/cli'
2
2
 
3
3
  /**
4
4
  * Stone build configuration.
5
5
  */
6
- export default defineConfig({
6
+ export default defineBuilderConfig({
7
7
  dotenv: {
8
8
  private: {
9
9
  path: ['.env', '.env.public'],
@@ -0,0 +1,30 @@
1
+ import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
2
+
3
+ /**
4
+ * The whole application, booted in memory and asked a real question.
5
+ *
6
+ * Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
7
+ * through the same kernel production runs. If this passes the application works; if it fails the
8
+ * application is broken. That is the only kind of test worth shipping in a starter — the previous one
9
+ * stubbed out the framework's own decorators, which meant it could pass while nothing worked.
10
+ */
11
+ describe('Pages', () => {
12
+ it('renders the home page, data included', async () => {
13
+ const app = await createTestApp()
14
+
15
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
16
+
17
+ expect(response.statusCode).toBe(200)
18
+ // A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
19
+ // whatever you already use (happy-dom, jsdom, Testing Library): none is bundled here.
20
+ expect(response.html()).toContain('Ada')
21
+ })
22
+
23
+ it('renders without a name', async () => {
24
+ const app = await createTestApp()
25
+
26
+ const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
27
+
28
+ expect(response.statusCode).toBe(200)
29
+ })
30
+ })