@oyolincc/create 0.0.0-dev-20260922064135 → 0.0.0-dev-20261006065712

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 (126) hide show
  1. package/package.json +1 -1
  2. package/templates/hono-fullstack/.vscode/launch.json +14 -3
  3. package/templates/hono-fullstack/.vscode/settings.json +3 -1
  4. package/templates/hono-fullstack/AGENTS.md +7 -0
  5. package/templates/hono-fullstack/README.md +28 -14
  6. package/templates/hono-fullstack/_gitignore +4 -0
  7. package/templates/hono-fullstack/apps/server/.dev.vars.example +9 -0
  8. package/templates/hono-fullstack/apps/server/package.json +17 -9
  9. package/templates/hono-fullstack/apps/server/src/adapters/cloudflare/entry.ts +36 -0
  10. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/bootstrap.ts +3 -6
  11. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/entry.ts +17 -5
  12. package/templates/hono-fullstack/apps/server/src/app.ts +2 -2
  13. package/templates/hono-fullstack/apps/server/src/config.ts +47 -0
  14. package/templates/hono-fullstack/apps/server/src/infra/logger/pino.ts +4 -0
  15. package/templates/hono-fullstack/apps/server/src/infra/work-runner/example/worker-entry.ts +4 -25
  16. package/templates/hono-fullstack/apps/server/src/infra/work-runner/pass-through/runner.ts +42 -0
  17. package/templates/{hono-server/apps/server/src/infra/work-runner/example → hono-fullstack/apps/server/src/infra/work-runner/piscina}/runner.ts +9 -9
  18. package/templates/hono-fullstack/apps/server/src/infra/work-runner/types.ts +7 -2
  19. package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/message.ts +7 -4
  20. package/templates/hono-fullstack/apps/server/src/modules/example/routes.ts +3 -5
  21. package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.test.ts +17 -5
  22. package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.ts +38 -15
  23. package/templates/hono-fullstack/apps/server/src/shared/app/types.ts +3 -4
  24. package/templates/hono-fullstack/apps/server/tsconfig.json +1 -0
  25. package/templates/hono-fullstack/apps/server/vite.config.ts +8 -3
  26. package/templates/hono-fullstack/apps/server/wrangler.jsonc +29 -0
  27. package/templates/hono-fullstack/apps/stack/backend/cloudflare.ts +5 -0
  28. package/templates/hono-fullstack/apps/stack/backend/node.ts +3 -0
  29. package/templates/hono-fullstack/apps/stack/frontend/components/general/ErrorMessage.tsx +4 -0
  30. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/AppLayout.tsx +11 -0
  31. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/Header.tsx +7 -0
  32. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/Page.tsx +5 -0
  33. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/PageContainer.tsx +5 -0
  34. package/templates/hono-fullstack/apps/stack/frontend/modules/example/components/ExampleForm.tsx +45 -0
  35. package/templates/hono-fullstack/apps/stack/frontend/modules/example/components/ExampleList.tsx +13 -0
  36. package/templates/hono-fullstack/apps/stack/frontend/routeTree.gen.ts +21 -4
  37. package/templates/hono-fullstack/apps/stack/frontend/routes/api/$.ts +18 -0
  38. package/templates/hono-fullstack/apps/stack/frontend/routes/index.tsx +48 -2
  39. package/templates/hono-fullstack/apps/stack/frontend/vite-env.d.ts +5 -0
  40. package/templates/hono-fullstack/apps/stack/package.json +20 -5
  41. package/templates/hono-fullstack/apps/stack/tsconfig.json +2 -1
  42. package/templates/hono-fullstack/apps/stack/vite.config.ts +75 -26
  43. package/templates/hono-fullstack/apps/stack/wrangler.jsonc +20 -0
  44. package/templates/hono-fullstack/envs/.env.example.server +1 -1
  45. package/templates/hono-fullstack/envs/.env.example.sqlite-d1 +3 -0
  46. package/templates/hono-fullstack/libs/db/drizzle-kit/sqlite-d1.config.ts +22 -0
  47. package/templates/hono-fullstack/libs/db/package.json +8 -6
  48. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/d1-client.ts +12 -0
  49. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/columns.ts +5 -0
  50. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/modules/examples/examples.ts +2 -2
  51. package/templates/hono-fullstack/libs/db/vite.config.ts +1 -0
  52. package/templates/hono-fullstack/libs/definition/src/configs/defs/runtime.ts +1 -0
  53. package/templates/hono-fullstack/libs/definition/src/configs/defs/sqlite.ts +16 -3
  54. package/templates/hono-fullstack/oyo.config.json +2 -1
  55. package/templates/hono-fullstack/package.json +1 -1
  56. package/templates/hono-fullstack/pnpm-workspace.yaml +7 -5
  57. package/templates/hono-fullstack/turbo.json +25 -3
  58. package/templates/hono-fullstack/vite.config.ts +1 -0
  59. package/templates/hono-server/.vscode/launch.json +7 -3
  60. package/templates/hono-server/.vscode/settings.json +3 -1
  61. package/templates/hono-server/AGENTS.md +7 -0
  62. package/templates/hono-server/README.md +25 -12
  63. package/templates/hono-server/_gitignore +4 -0
  64. package/templates/hono-server/apps/server/.dev.vars.example +9 -0
  65. package/templates/hono-server/apps/server/package.json +18 -8
  66. package/templates/hono-server/apps/server/src/adapters/cloudflare/entry.ts +36 -0
  67. package/templates/hono-server/apps/server/src/adapters/nodejs/bootstrap.ts +3 -3
  68. package/templates/hono-server/apps/server/src/adapters/nodejs/entry.ts +24 -4
  69. package/templates/hono-server/apps/server/src/app.ts +2 -2
  70. package/templates/hono-server/apps/server/src/config.ts +47 -0
  71. package/templates/hono-server/apps/server/src/infra/logger/pino.ts +4 -0
  72. package/templates/hono-server/apps/server/src/infra/work-runner/example/worker-entry.ts +4 -25
  73. package/templates/hono-server/apps/server/src/infra/work-runner/pass-through/runner.ts +42 -0
  74. package/templates/{hono-fullstack/apps/server/src/infra/work-runner/example → hono-server/apps/server/src/infra/work-runner/piscina}/runner.ts +9 -9
  75. package/templates/hono-server/apps/server/src/infra/work-runner/types.ts +7 -2
  76. package/templates/hono-server/apps/server/src/infra/work-runner/utils/message.ts +7 -4
  77. package/templates/hono-server/apps/server/src/modules/example/contracts.ts +4 -0
  78. package/templates/hono-server/apps/server/src/modules/example/routes.ts +32 -5
  79. package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.test.ts +17 -5
  80. package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.ts +38 -15
  81. package/templates/hono-server/apps/server/src/shared/app/types.ts +3 -4
  82. package/templates/hono-server/apps/server/tsconfig.json +1 -0
  83. package/templates/hono-server/apps/server/vite.config.ts +8 -3
  84. package/templates/hono-server/apps/server/wrangler.jsonc +29 -0
  85. package/templates/hono-server/docker/server.Dockerfile +3 -5
  86. package/templates/hono-server/envs/.env.example.server +1 -1
  87. package/templates/hono-server/envs/.env.example.sqlite +1 -0
  88. package/templates/hono-server/envs/.env.example.sqlite-d1 +3 -0
  89. package/templates/hono-server/libs/db/drizzle-kit/sqlite-d1.config.ts +22 -0
  90. package/templates/hono-server/libs/db/drizzle-kit/sqlite.config.ts +22 -0
  91. package/templates/hono-server/libs/db/package.json +42 -0
  92. package/templates/hono-server/libs/db/src/dialects/sqlite/client.ts +35 -0
  93. package/templates/hono-server/libs/db/src/dialects/sqlite/d1-client.ts +12 -0
  94. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/columns.ts +37 -0
  95. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/index.ts +7 -0
  96. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/modules/examples/examples.ts +22 -0
  97. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/relations.ts +5 -0
  98. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/tables.ts +5 -0
  99. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/types.ts +7 -0
  100. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/vo.ts +1 -0
  101. package/templates/hono-server/libs/db/src/dialects/sqlite/utils.ts +8 -0
  102. package/templates/hono-server/libs/db/src/shared/types.ts +26 -0
  103. package/templates/hono-server/libs/db/tsconfig.json +10 -0
  104. package/templates/hono-server/libs/db/vite.config.ts +22 -0
  105. package/templates/hono-server/libs/definition/src/configs/defs/runtime.ts +1 -0
  106. package/templates/hono-server/libs/definition/src/configs/defs/sqlite.ts +26 -0
  107. package/templates/hono-server/libs/definition/src/configs/index.ts +1 -0
  108. package/templates/hono-server/oyo.config.json +3 -1
  109. package/templates/hono-server/package.json +2 -3
  110. package/templates/hono-server/pnpm-workspace.yaml +12 -3
  111. package/templates/hono-server/turbo.json +25 -3
  112. package/templates/hono-server/vite.config.ts +1 -0
  113. package/templates/monorepo/.vscode/launch.json +0 -3
  114. package/templates/monorepo/.vscode/settings.json +3 -1
  115. package/templates/monorepo/AGENTS.md +7 -0
  116. package/templates/monorepo/_gitignore +4 -0
  117. package/templates/monorepo/pnpm-workspace.yaml +6 -3
  118. package/templates/monorepo/turbo.json +25 -3
  119. package/templates/monorepo/vite.config.ts +1 -0
  120. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/config.ts +0 -53
  121. package/templates/hono-fullstack/apps/server/src/infra/work-runner/example/runner.test.ts +0 -56
  122. package/templates/hono-fullstack/apps/stack/frontend/modules/example/components/ExamplePage.tsx +0 -59
  123. package/templates/hono-fullstack/apps/stack/nitro/handlers/hono.ts +0 -1
  124. package/templates/hono-fullstack/apps/stack/nitro/plugins/hono.ts +0 -9
  125. package/templates/hono-server/apps/server/src/adapters/nodejs/config.ts +0 -53
  126. package/templates/hono-server/apps/server/src/infra/work-runner/example/runner.test.ts +0 -56
@@ -1,9 +1,15 @@
1
1
  import type { ProcessExampleInput, ProcessExampleOutput } from '@repo/definition/backend'
2
2
  import { type WorkSubject } from '@/infra/work-runner/constants'
3
- import type { DefineRunnerTaskModel, RunnerTaskResult } from '@/infra/work-runner/types'
4
- import { WorkerTaskBusinessError } from '@/infra/work-runner/utils/business-error'
3
+ import type {
4
+ DefineRunnerTaskModel,
5
+ RunnerTaskResult,
6
+ WorkerMessagePort,
7
+ } from '@/infra/work-runner/types'
8
+ import {
9
+ isWorkerTaskBusinessError,
10
+ WorkerTaskBusinessError,
11
+ } from '@/infra/work-runner/utils/business-error'
5
12
  import { workerLog, workerProgress } from '@/infra/work-runner/utils/message'
6
- import type { MessagePort } from 'node:worker_threads'
7
13
 
8
14
  export type ExampleTaskErrorCode = 'EXAMPLE_PROCESS_FAILED'
9
15
 
@@ -16,20 +22,37 @@ export type ExampleRunnerTaskModel = DefineRunnerTaskModel<
16
22
 
17
23
  export async function executeExampleTask(
18
24
  input: ExampleRunnerTaskModel['input'],
19
- messagePort: MessagePort,
25
+ messagePort: WorkerMessagePort,
20
26
  ): Promise<RunnerTaskResult<ProcessExampleOutput, ExampleTaskErrorCode>> {
21
- workerLog(messagePort, 'debug', 'Example task started')
22
- workerProgress(messagePort, 0, 'Validating input')
27
+ try {
28
+ workerLog(messagePort, 'debug', 'Example task started')
29
+ workerProgress(messagePort, 0, 'Validating input')
23
30
 
24
- const original = input.message
25
- const normalized = original.trim().replace(/\s+/g, ' ')
26
- if (!normalized) {
27
- throw new WorkerTaskBusinessError('Message must not be empty', { field: 'message' })
28
- }
31
+ const original = input.message
32
+ const normalized = original.trim().replace(/\s+/g, ' ')
33
+ if (!normalized) {
34
+ throw new WorkerTaskBusinessError('Message must not be empty', { field: 'message' })
35
+ }
36
+
37
+ workerProgress(messagePort, 100, 'Example task completed')
38
+ const result = {
39
+ ok: true,
40
+ data: { original, normalized, length: normalized.length },
41
+ } as const
42
+ messagePort.postMessage({ type: 'result', data: result.data })
43
+ return result
44
+ } catch (error) {
45
+ if (!isWorkerTaskBusinessError(error)) throw error
29
46
 
30
- workerProgress(messagePort, 100, 'Example task completed')
31
- return {
32
- ok: true,
33
- data: { original, normalized, length: normalized.length },
47
+ const result: RunnerTaskResult<ProcessExampleOutput, ExampleTaskErrorCode> = {
48
+ ok: false,
49
+ error: {
50
+ code: 'EXAMPLE_PROCESS_FAILED',
51
+ message: error.message,
52
+ ...(error.details ? { details: error.details } : {}),
53
+ },
54
+ }
55
+ messagePort.postMessage({ type: 'error', error: result.error })
56
+ return result
34
57
  }
35
58
  }
@@ -30,9 +30,8 @@ export type AppConfig = {
30
30
  export type AppDependencies = {
31
31
  appConfig: AppConfig
32
32
  loggerHandler: MiddlewareHandler<{ Variables: { logger: AppLogger } }>
33
- exampleWorkRunner: AppWorkRunner
34
- /** Fullstack injects its SQLite database; standalone intentionally leaves this absent. */
35
- db?: unknown
33
+ workRunner: AppWorkRunner
34
+ db: unknown
36
35
  }
37
36
 
38
37
  export type AppEnv = {
@@ -40,6 +39,6 @@ export type AppEnv = {
40
39
  }
41
40
 
42
41
  export type CreateAppOptions = {
43
- basePath?: string
42
+ serverBasePath: string
44
43
  makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies
45
44
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "@oyolincc/dev-config/tsconfig.bundler.json",
3
3
  "compilerOptions": {
4
+ "types": ["node", "./worker-configuration.d.ts"],
4
5
  "verbatimModuleSyntax": true,
5
6
  "jsx": "react-jsx",
6
7
  "jsxImportSource": "hono/jsx",
@@ -52,14 +52,19 @@ export default defineConfig(() => {
52
52
  pack: {
53
53
  ...packConfig,
54
54
  plugins: [virtualWorkers('pack')],
55
+ deps: {
56
+ neverBundle: ['cloudflare:workers'],
57
+ },
55
58
  entry: {
56
59
  ...(isServe
57
60
  ? {}
58
61
  : {
59
- nodeMain: './src/adapters/nodejs/bootstrap.ts',
62
+ nodejsMain: './src/adapters/nodejs/bootstrap.ts',
60
63
  }),
61
- nodeEntry: './src/adapters/nodejs/entry.ts',
62
- 'workers/example-worker': './src/infra/work-runner/example/worker-entry.ts',
64
+ nodejsEntry: './src/adapters/nodejs/entry.ts',
65
+ cloudflareEntry: './src/adapters/cloudflare/entry.ts',
66
+ // Enable this entry together with PiscinaWorkRunner in the Node.js adapter.
67
+ // 'workers/example-worker': './src/infra/work-runner/example/worker-entry.ts',
63
68
  },
64
69
  // deps: {
65
70
  // neverBundle: [...builtinModules],
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "./node_modules/wrangler/config-schema.json",
3
+ "name": "repo-server-dev",
4
+ "main": "./src/adapters/cloudflare/entry.ts",
5
+ "compatibility_date": "2026-09-23",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ "dev": {
8
+ "port": 6464,
9
+ "inspector_port": 19230,
10
+ },
11
+ "observability": {
12
+ "logs": {
13
+ "enabled": true,
14
+ "invocation_logs": true,
15
+ },
16
+ "traces": {
17
+ "enabled": false,
18
+ },
19
+ },
20
+ "d1_databases": [
21
+ {
22
+ "binding": "DB",
23
+ "database_name": "repo-db",
24
+ "database_id": "00000000-0000-0000-0000-000000000000",
25
+ "migrations_dir": "../../libs/db/migrations/sqlite-d1",
26
+ "migrations_pattern": "../../libs/db/migrations/sqlite-d1/*/migration.sql",
27
+ },
28
+ ],
29
+ }
@@ -0,0 +1,5 @@
1
+ import app from '@repo/server/cloudflare'
2
+ import { env } from 'cloudflare:workers'
3
+
4
+ export const handleApiRequest = async (request: Request): Promise<Response> =>
5
+ app.fetch(request, env)
@@ -0,0 +1,3 @@
1
+ import app from '@repo/server/nodejs'
2
+
3
+ export const handleApiRequest = async (request: Request): Promise<Response> => app.fetch(request)
@@ -0,0 +1,4 @@
1
+ export function ErrorMessage({ error }: { error: unknown }) {
2
+ if (!error) return null
3
+ return <p className="text-red-400">{String(error)}</p>
4
+ }
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react'
2
+ import { Header } from './Header'
3
+
4
+ export function AppLayout({ children }: { children: ReactNode }) {
5
+ return (
6
+ <div className="min-h-screen bg-zinc-950 text-zinc-100">
7
+ <Header />
8
+ {children}
9
+ </div>
10
+ )
11
+ }
@@ -0,0 +1,7 @@
1
+ export function Header() {
2
+ return (
3
+ <header className="border-b border-zinc-800 px-8 py-4">
4
+ <strong>Hono fullstack</strong>
5
+ </header>
6
+ )
7
+ }
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export function Page({ children }: { children: ReactNode }) {
4
+ return <section className="flex flex-col gap-6">{children}</section>
5
+ }
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export function PageContainer({ children }: { children: ReactNode }) {
4
+ return <main className="mx-auto max-w-2xl p-8">{children}</main>
5
+ }
@@ -0,0 +1,45 @@
1
+ import type { FormEvent } from 'react'
2
+
3
+ interface ExampleFormProps {
4
+ message: string
5
+ saving: boolean
6
+ processing: boolean
7
+ onMessageChange: (message: string) => void
8
+ onSave: () => Promise<void>
9
+ onProcess: () => void
10
+ }
11
+
12
+ export function ExampleForm({
13
+ message,
14
+ saving,
15
+ processing,
16
+ onMessageChange,
17
+ onSave,
18
+ onProcess,
19
+ }: ExampleFormProps) {
20
+ const submit = async (event: FormEvent) => {
21
+ event.preventDefault()
22
+ await onSave()
23
+ }
24
+
25
+ return (
26
+ <form className="flex gap-2" onSubmit={submit}>
27
+ <input
28
+ className="min-w-0 flex-1 rounded border border-zinc-700 bg-zinc-900 px-3 py-2"
29
+ value={message}
30
+ onChange={(event) => onMessageChange(event.target.value)}
31
+ placeholder="Message"
32
+ />
33
+ <button className="rounded bg-blue-600 px-4 py-2" disabled={!message.trim() || saving}>
34
+ Save
35
+ </button>
36
+ <button
37
+ className="rounded bg-emerald-600 px-4 py-2"
38
+ type="button"
39
+ disabled={!message.trim() || processing}
40
+ onClick={onProcess}>
41
+ Process
42
+ </button>
43
+ </form>
44
+ )
45
+ }
@@ -0,0 +1,13 @@
1
+ import type { Example } from '@repo/definition/frontend'
2
+
3
+ export function ExampleList({ examples }: { examples?: Example[] }) {
4
+ return (
5
+ <ul className="grid gap-2">
6
+ {examples?.map((example) => (
7
+ <li className="rounded border border-zinc-800 p-3" key={example.id}>
8
+ {example.message}
9
+ </li>
10
+ ))}
11
+ </ul>
12
+ )
13
+ }
@@ -3,33 +3,43 @@
3
3
  // This file was automatically generated by TanStack Router.
4
4
  import { Route as rootRouteImport } from './routes/__root'
5
5
  import { Route as IndexRouteImport } from './routes/index'
6
+ import { Route as ApiSplatRouteImport } from './routes/api/$'
6
7
 
7
8
  const IndexRoute = IndexRouteImport.update({
8
9
  id: '/',
9
10
  path: '/',
10
11
  getParentRoute: () => rootRouteImport,
11
12
  } as any)
13
+ const ApiSplatRoute = ApiSplatRouteImport.update({
14
+ id: '/api/$',
15
+ path: '/api/$',
16
+ getParentRoute: () => rootRouteImport,
17
+ } as any)
12
18
 
13
19
  export interface FileRoutesByFullPath {
14
20
  '/': typeof IndexRoute
21
+ '/api/$': typeof ApiSplatRoute
15
22
  }
16
23
  export interface FileRoutesByTo {
17
24
  '/': typeof IndexRoute
25
+ '/api/$': typeof ApiSplatRoute
18
26
  }
19
27
  export interface FileRoutesById {
20
28
  __root__: typeof rootRouteImport
21
29
  '/': typeof IndexRoute
30
+ '/api/$': typeof ApiSplatRoute
22
31
  }
23
32
  export interface FileRouteTypes {
24
33
  fileRoutesByFullPath: FileRoutesByFullPath
25
- fullPaths: '/'
34
+ fullPaths: '/' | '/api/$'
26
35
  fileRoutesByTo: FileRoutesByTo
27
- to: '/'
28
- id: '__root__' | '/'
36
+ to: '/' | '/api/$'
37
+ id: '__root__' | '/' | '/api/$'
29
38
  fileRoutesById: FileRoutesById
30
39
  }
31
40
  export interface RootRouteChildren {
32
41
  IndexRoute: typeof IndexRoute
42
+ ApiSplatRoute: typeof ApiSplatRoute
33
43
  }
34
44
  declare module '@tanstack/react-router' {
35
45
  interface FileRoutesByPath {
@@ -40,9 +50,16 @@ declare module '@tanstack/react-router' {
40
50
  preLoaderRoute: typeof IndexRouteImport
41
51
  parentRoute: typeof rootRouteImport
42
52
  }
53
+ '/api/$': {
54
+ id: '/api/$'
55
+ path: '/api/$'
56
+ fullPath: '/api/$'
57
+ preLoaderRoute: typeof ApiSplatRouteImport
58
+ parentRoute: typeof rootRouteImport
59
+ }
43
60
  }
44
61
  }
45
- const rootRouteChildren: RootRouteChildren = { IndexRoute }
62
+ const rootRouteChildren: RootRouteChildren = { IndexRoute, ApiSplatRoute }
46
63
  export const routeTree = rootRouteImport
47
64
  ._addFileChildren(rootRouteChildren)
48
65
  ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,18 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { handleApiRequest } from '#server-runtime'
3
+
4
+ const serve = async ({ request }: { request: Request }) => handleApiRequest(request)
5
+
6
+ export const Route = createFileRoute('/api/$')({
7
+ server: {
8
+ handlers: {
9
+ GET: serve,
10
+ POST: serve,
11
+ PUT: serve,
12
+ PATCH: serve,
13
+ DELETE: serve,
14
+ OPTIONS: serve,
15
+ HEAD: serve,
16
+ },
17
+ },
18
+ })
@@ -1,4 +1,50 @@
1
1
  import { createFileRoute } from '@tanstack/react-router'
2
- import { ExamplePage } from '#/modules/example/components/ExamplePage'
2
+ import { useAtom } from 'jotai'
3
+ import { ErrorMessage } from '#/components/general/ErrorMessage'
4
+ import { AppLayout } from '#/components/layouts/AppLayout'
5
+ import { Page } from '#/components/layouts/Page'
6
+ import { PageContainer } from '#/components/layouts/PageContainer'
7
+ import { ExampleForm } from '#/modules/example/components/ExampleForm'
8
+ import { ExampleList } from '#/modules/example/components/ExampleList'
9
+ import { useCreateExample, useExamples, useProcessExample } from '#/modules/example/hooks/api-hooks'
10
+ import { exampleMessageAtom } from '#/modules/example/stores'
3
11
 
4
- export const Route = createFileRoute('/')({ component: ExamplePage })
12
+ export const Route = createFileRoute('/')({ component: Home })
13
+
14
+ function Home() {
15
+ const [message, setMessage] = useAtom(exampleMessageAtom)
16
+ const examples = useExamples()
17
+ const create = useCreateExample()
18
+ const process = useProcessExample()
19
+
20
+ const save = async () => {
21
+ await create.mutateAsync({ message })
22
+ setMessage('')
23
+ }
24
+
25
+ return (
26
+ <AppLayout>
27
+ <PageContainer>
28
+ <Page>
29
+ <header>
30
+ <h1 className="text-3xl font-bold">Example workspace</h1>
31
+ <p className="text-zinc-400">Nitro mounts the independent Hono server under /api.</p>
32
+ </header>
33
+ <ExampleForm
34
+ message={message}
35
+ saving={create.isPending}
36
+ processing={process.isPending}
37
+ onMessageChange={setMessage}
38
+ onSave={save}
39
+ onProcess={() => process.mutate({ message })}
40
+ />
41
+ {process.data && (
42
+ <pre className="rounded bg-zinc-900 p-4">{JSON.stringify(process.data, null, 2)}</pre>
43
+ )}
44
+ <ErrorMessage error={create.error || process.error || examples.error} />
45
+ <ExampleList examples={examples.data} />
46
+ </Page>
47
+ </PageContainer>
48
+ </AppLayout>
49
+ )
50
+ }
@@ -1 +1,6 @@
1
1
  /// <reference types="vite/client" />
2
+ /// <reference types="@tanstack/react-start" />
3
+
4
+ declare module '#server-runtime' {
5
+ export const handleApiRequest: (request: Request) => Promise<Response>
6
+ }
@@ -4,11 +4,24 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "oyo env server -- vp dev",
8
- "dev:chain": "turbo watch dev --filter=@repo/stack",
7
+ "dev": "pnpm dev:node",
8
+ "build": "pnpm build:node",
9
+ "preview": "pnpm preview:node",
10
+ "dev:node-chain": "turbo watch dev:node --filter=@repo/stack",
11
+ "dev:node": "DEPLOY_TARGET=node SERVER_BASE_PATH=/api oyo env server -- vp dev",
12
+ "build:node-chain": "turbo build:node --filter=@repo/stack",
13
+ "build:node": "DEPLOY_TARGET=node SERVER_BASE_PATH=/api oyo env server -- vp build",
14
+ "preview:node": "DEPLOY_TARGET=node SERVER_BASE_PATH=/api oyo env server -- vp preview",
15
+ "dev:cloudflare-chain": "turbo watch dev:cloudflare --filter=@repo/stack",
16
+ "dev:cloudflare": "DEPLOY_TARGET=cloudflare vp dev",
17
+ "build:cloudflare-chain": "turbo build:cloudflare --filter=@repo/stack",
18
+ "build:cloudflare": "DEPLOY_TARGET=cloudflare vp build",
19
+ "preview:cloudflare": "DEPLOY_TARGET=cloudflare vp preview",
20
+ "deploy:cloudflare": "pnpm build:cloudflare-chain && wrangler deploy",
21
+ "db:migrate:cloudflare:local": "wrangler d1 migrations apply DB --local --config wrangler.jsonc --persist-to ../../.wrangler/state",
22
+ "cf:typegen": "wrangler types",
23
+ "cf:typecheck": "wrangler types --check",
9
24
  "generate-routes": "tsr generate",
10
- "build": "vp build",
11
- "preview": "oyo env server -- vp preview",
12
25
  "test": "vp test",
13
26
  "check": "vp check"
14
27
  },
@@ -25,6 +38,7 @@
25
38
  "react-dom": "catalog:frontend"
26
39
  },
27
40
  "devDependencies": {
41
+ "@cloudflare/vite-plugin": "catalog:",
28
42
  "@oyolincc/cli": "catalog:",
29
43
  "@oyolincc/dev-config": "catalog:",
30
44
  "@rollup/pluginutils": "catalog:",
@@ -39,6 +53,7 @@
39
53
  "fast-glob": "catalog:",
40
54
  "tailwindcss": "catalog:frontend",
41
55
  "typescript": "catalog:",
42
- "vite-plus": "catalog:"
56
+ "vite-plus": "catalog:",
57
+ "wrangler": "catalog:"
43
58
  }
44
59
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "@oyolincc/dev-config/tsconfig.frontend.json",
3
3
  "compilerOptions": {
4
+ "types": ["./worker-configuration.d.ts"],
4
5
  "jsx": "react-jsx",
5
6
  "paths": {
6
7
  "#/*": ["./frontend/*"],
@@ -8,5 +9,5 @@
8
9
  }
9
10
  },
10
11
  "files": [],
11
- "include": ["frontend", "nitro", "./*.ts", "./*.mts"]
12
+ "include": ["frontend", "backend", "nitro", "./*.ts", "./*.mts"]
12
13
  }
@@ -1,33 +1,82 @@
1
- import { defineConfig } from 'vite-plus'
1
+ import { defineConfig, lazyPlugins, type PluginOption } from 'vite-plus'
2
2
  import { tanstackStart } from '@tanstack/react-start/plugin/vite'
3
3
  import tailwindcss from '@tailwindcss/vite'
4
4
  import viteReact from '@vitejs/plugin-react'
5
- import { nitro } from 'nitro/vite'
5
+ import path from 'node:path'
6
6
 
7
- export default defineConfig({
8
- server: {
9
- port: 3000,
10
- warmup: {
11
- clientFiles: ['./frontend/router.tsx'],
12
- ssrFiles: ['./frontend/router.tsx'],
7
+ export default defineConfig(() => {
8
+ const deployTarget = process.env.DEPLOY_TARGET || 'node'
9
+ if (deployTarget !== 'node' && deployTarget !== 'cloudflare') {
10
+ throw new Error(`DEPLOY_TARGET must be "node" or "cloudflare", received: ${deployTarget}`)
11
+ }
12
+ const serverExternal = /^@repo\/server(?:\/|$)/
13
+
14
+ return {
15
+ ...(deployTarget === 'node'
16
+ ? {
17
+ environments: {
18
+ ssr: {
19
+ resolve: {
20
+ // 拦住第一次:Vite/TanStack SSR bundle
21
+ external: ['@repo/server/nodejs'],
22
+ },
23
+ },
24
+ },
25
+ }
26
+ : {
27
+ environments: {
28
+ ssr: {
29
+ resolve: {
30
+ mainFields: ['browser', 'module', 'jsnext:main', 'jsnext'],
31
+ },
32
+ },
33
+ },
34
+ }),
35
+ server: {
36
+ port: 3000,
37
+ warmup: {
38
+ clientFiles: ['./frontend/router.tsx'],
39
+ ssrFiles: ['./frontend/router.tsx'],
40
+ },
41
+ },
42
+ preview: {
43
+ port: 3000,
44
+ strictPort: true,
13
45
  },
14
- },
15
- resolve: { tsconfigPaths: true },
16
- optimizeDeps: { include: ['@repo/definition/frontend'] },
17
- plugins: [
18
- nitro({
19
- serverDir: './nitro',
20
- handlers: [
21
- {
22
- route: '/api/**',
23
- handler: './nitro/handlers/hono.ts',
24
- middleware: false,
25
- lazy: false,
26
- },
27
- ],
46
+ resolve: {
47
+ tsconfigPaths: true,
48
+ alias: {
49
+ '#server-runtime': path.resolve(import.meta.dirname, `./backend/${deployTarget}.ts`),
50
+ },
51
+ },
52
+ optimizeDeps: { include: ['@repo/definition/frontend'] },
53
+ plugins: lazyPlugins(async () => {
54
+ const runtimePlugins =
55
+ deployTarget === 'cloudflare'
56
+ ? [
57
+ (await import('@cloudflare/vite-plugin')).cloudflare({
58
+ viteEnvironment: { name: 'ssr' },
59
+ persistState: {
60
+ path: path.resolve(import.meta.dirname, '../../.wrangler/state'),
61
+ },
62
+ }),
63
+ ]
64
+ : (await import('nitro/vite')).nitro({
65
+ preset: 'node_server',
66
+ serverDir: './nitro',
67
+ // 拦住第二次:Nitro 最终 server bundle
68
+ rollupConfig: {
69
+ external: [serverExternal],
70
+ },
71
+ })
72
+
73
+ const pluginOptions = [
74
+ ...runtimePlugins,
75
+ ...tanstackStart({ srcDirectory: './frontend' }),
76
+ ...tailwindcss(),
77
+ ...viteReact(),
78
+ ] as PluginOption[]
79
+ return pluginOptions
28
80
  }),
29
- tanstackStart({ srcDirectory: './frontend' }),
30
- tailwindcss(),
31
- viteReact(),
32
- ],
81
+ }
33
82
  })
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "./node_modules/wrangler/config-schema.json",
3
+ "name": "repo-stack",
4
+ "main": "@tanstack/react-start/server-entry",
5
+ "compatibility_date": "2026-09-23",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ "observability": {
8
+ "logs": { "enabled": true, "invocation_logs": true },
9
+ "traces": { "enabled": false },
10
+ },
11
+ "d1_databases": [
12
+ {
13
+ "binding": "DB",
14
+ "database_name": "repo-db",
15
+ "database_id": "00000000-0000-0000-0000-000000000000",
16
+ "migrations_dir": "../../libs/db/migrations/sqlite-d1",
17
+ "migrations_pattern": "../../libs/db/migrations/sqlite-d1/*/migration.sql",
18
+ },
19
+ ],
20
+ }
@@ -1,4 +1,5 @@
1
1
  NODE_PORT=6464
2
+ SERVER_BASE_PATH=/api
2
3
  SERVER_ENV=local
3
4
  LOGGER_DIR=./logs
4
5
  LOGGER_FILE_BASE=app
@@ -9,4 +10,3 @@ CORS_DOMAINS='*'
9
10
  # CONCURRENT_TASKS_PER_WORKER=1
10
11
  # MAX_QUEUE=12
11
12
  # WORKER_TASK_TIMEOUT_MS=30000
12
-
@@ -0,0 +1,3 @@
1
+ CLOUDFLARE_ACCOUNT_ID=
2
+ CLOUDFLARE_DATABASE_ID=
3
+ CLOUDFLARE_TOKEN=
@@ -0,0 +1,22 @@
1
+ import path from 'node:path'
2
+ import { defineConfig } from 'drizzle-kit'
3
+ import { d1SchemaConfig } from '@repo/definition/backend'
4
+
5
+ const configDir = import.meta.dirname
6
+
7
+ const { cloudflareAccountId, cloudflareDatabaseId, cloudflareToken } = d1SchemaConfig.resolve({
8
+ context: process.env,
9
+ matchContextKey: 'constantCase',
10
+ })
11
+
12
+ export default defineConfig({
13
+ dialect: 'sqlite',
14
+ driver: 'd1-http',
15
+ schema: path.join(configDir, '../src/dialects/sqlite/schema/tables.ts'),
16
+ out: path.join(configDir, '../migrations/sqlite-d1'),
17
+ dbCredentials: {
18
+ accountId: cloudflareAccountId,
19
+ databaseId: cloudflareDatabaseId,
20
+ token: cloudflareToken,
21
+ },
22
+ })