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

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 (29) hide show
  1. package/package.json +1 -1
  2. package/templates/hono-fullstack/apps/server/package.json +1 -2
  3. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/entry.ts +6 -3
  4. package/templates/hono-fullstack/apps/server/src/app.ts +4 -6
  5. package/templates/hono-fullstack/apps/server/src/shared/app/types.ts +6 -1
  6. package/templates/hono-fullstack/apps/server/src/shared/network/validator.ts +10 -3
  7. package/templates/hono-fullstack/apps/stack/frontend/modules/example/{ExamplePage.tsx → components/ExamplePage.tsx} +5 -3
  8. package/templates/hono-fullstack/apps/stack/frontend/modules/example/{hooks.ts → hooks/api-hooks.ts} +2 -1
  9. package/templates/hono-fullstack/apps/stack/frontend/modules/example/stores/index.ts +3 -0
  10. package/templates/hono-fullstack/apps/stack/frontend/routes/index.tsx +1 -1
  11. package/templates/hono-fullstack/apps/stack/package.json +2 -1
  12. package/templates/hono-fullstack/apps/stack/tsconfig.json +6 -4
  13. package/templates/hono-fullstack/apps/stack/tsr.config.json +2 -2
  14. package/templates/hono-fullstack/envs/.env.example.sqlite +1 -2
  15. package/templates/hono-fullstack/libs/db/package.json +1 -0
  16. package/templates/hono-fullstack/package.json +1 -5
  17. package/templates/hono-fullstack/pnpm-workspace.yaml +1 -1
  18. package/templates/hono-fullstack/turbo.json +1 -0
  19. package/templates/hono-server/apps/server/package.json +1 -2
  20. package/templates/hono-server/apps/server/src/adapters/nodejs/entry.ts +5 -3
  21. package/templates/hono-server/apps/server/src/app.ts +4 -6
  22. package/templates/hono-server/apps/server/src/shared/app/types.ts +6 -1
  23. package/templates/hono-server/apps/server/src/shared/network/validator.ts +10 -3
  24. package/templates/hono-server/libs/definition/vite.config.ts +1 -1
  25. package/templates/hono-server/pnpm-workspace.yaml +1 -1
  26. package/templates/hono-server/turbo.json +1 -0
  27. package/templates/monorepo/turbo.json +1 -0
  28. package/templates/hono-fullstack/apps/server/src/app.test.ts +0 -52
  29. package/templates/hono-server/apps/server/src/app.test.ts +0 -52
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oyolincc/create",
3
- "version": "0.0.0-dev-20260918103756",
3
+ "version": "0.0.0-dev-20260922064135",
4
4
  "description": "@oyolincc/create",
5
5
  "homepage": "https://github.com/oyolincc/lib#readme",
6
6
  "bugs": {
@@ -12,8 +12,7 @@
12
12
  },
13
13
  "scripts": {
14
14
  "dev": "concurrently -k -n dev,pack -c blue,green \"vp dev\" \"VITE_PUBLIC_DEV_SERVE=1 vp pack --watch\"",
15
- "dev:pack": "VITE_PUBLIC_DEV_SERVE=1 vp pack --watch",
16
- "start": "oyo env server -- node dist/nodeMain.mjs",
15
+ "dev:chain": "turbo watch dev --filter=@repo/server",
17
16
  "build": "vp pack",
18
17
  "test": "vp test",
19
18
  "check": "vp check"
@@ -39,9 +39,12 @@ export const exampleWorkRunner = ExampleWorkRunner.getInstance(appConfig.worker)
39
39
  export const sqliteClient = createSqliteClient(sqliteConfig.dbPath)
40
40
 
41
41
  let dependencies: AppDependencies | undefined
42
- const app = createApp(() => {
43
- dependencies ||= { appConfig, loggerHandler, exampleWorkRunner, db: sqliteClient.db }
44
- return dependencies
42
+ const app = createApp({
43
+ basePath: '/api',
44
+ makeDependencies: () => {
45
+ dependencies ||= { appConfig, loggerHandler, exampleWorkRunner, db: sqliteClient.db }
46
+ return dependencies
47
+ },
45
48
  })
46
49
 
47
50
  export default app
@@ -1,20 +1,18 @@
1
- import { type Context, Hono } from 'hono'
1
+ import { Hono } from 'hono'
2
2
  import { contextStorage } from 'hono/context-storage'
3
3
  import { csrf } from 'hono/csrf'
4
4
  import { secureHeaders } from 'hono/secure-headers'
5
5
  import { requestId } from 'hono/request-id'
6
6
  import { corsMiddleware } from './middlewares/cors'
7
7
  import { exampleRoutes } from './modules/example/routes'
8
- import type { AppDependencies, AppEnv } from './shared/app/types'
8
+ import type { CreateAppOptions, AppEnv } from './shared/app/types'
9
9
  import { E } from './shared/errors/defs'
10
10
  import { globalErrorHandler } from './shared/errors/handler'
11
11
  import { configureOpenAPI } from './shared/network/openapi'
12
12
  import { objectEntries } from './shared/utils'
13
13
 
14
- export const createApp = (
15
- makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies,
16
- ): Hono<AppEnv> => {
17
- const app = new Hono<AppEnv>()
14
+ export const createApp = ({ basePath, makeDependencies }: CreateAppOptions): Hono<AppEnv> => {
15
+ const app = new Hono<AppEnv>().basePath(basePath || '/')
18
16
 
19
17
  app.use('*', async (c, next) => {
20
18
  const deps = await makeDependencies(c)
@@ -8,7 +8,7 @@ import type {
8
8
  WorkerConfig,
9
9
  } from '@repo/definition/backend'
10
10
  import type { AppWorkRunner } from '@/infra/work-runner/types'
11
- import type { MiddlewareHandler } from 'hono'
11
+ import type { MiddlewareHandler, Context } from 'hono'
12
12
  import type { PinoLogger as HonoPinoLogger } from 'hono-pino'
13
13
  import type { RequestIdVariables } from 'hono/request-id'
14
14
 
@@ -38,3 +38,8 @@ export type AppDependencies = {
38
38
  export type AppEnv = {
39
39
  Variables: RequestIdVariables & AppDependencies & { logger?: AppLogger }
40
40
  }
41
+
42
+ export type CreateAppOptions = {
43
+ basePath?: string
44
+ makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies
45
+ }
@@ -1,6 +1,6 @@
1
- import type { ValidationTargets } from 'hono'
1
+ import type { Env, MiddlewareHandler, ValidationTargets } from 'hono'
2
2
  import { validator } from 'hono-openapi'
3
- import { flatten, type GenericSchema } from 'valibot'
3
+ import { flatten, type GenericSchema, type InferInput, type InferOutput } from 'valibot'
4
4
  import { E } from '@/shared/errors/defs'
5
5
 
6
6
  export const generalValidator = <
@@ -9,7 +9,14 @@ export const generalValidator = <
9
9
  >(
10
10
  target: TTarget,
11
11
  schema: TSchema,
12
- ) =>
12
+ ): MiddlewareHandler<
13
+ Env,
14
+ string,
15
+ {
16
+ in: { [K in TTarget]: InferInput<TSchema> }
17
+ out: { [K in TTarget]: InferOutput<TSchema> }
18
+ }
19
+ > =>
13
20
  validator(target, schema, (result, _c) => {
14
21
  if (!result.success) {
15
22
  throw E.NORMAL.VALIDATION.create({
@@ -1,8 +1,10 @@
1
- import { useState, type FormEvent } from 'react'
2
- import { useCreateExample, useExamples, useProcessExample } from './hooks'
1
+ import { useAtom } from 'jotai'
2
+ import type { FormEvent } from 'react'
3
+ import { useCreateExample, useExamples, useProcessExample } from '../hooks/api-hooks'
4
+ import { exampleMessageAtom } from '../stores'
3
5
 
4
6
  export function ExamplePage() {
5
- const [message, setMessage] = useState('')
7
+ const [message, setMessage] = useAtom(exampleMessageAtom)
6
8
  const examples = useExamples()
7
9
  const create = useCreateExample()
8
10
  const process = useProcessExample()
@@ -1,7 +1,8 @@
1
1
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
2
- import { createExample, listExamples, processExample } from './api'
2
+ import { createExample, listExamples, processExample } from '../api'
3
3
 
4
4
  export const exampleQueryKey = ['examples'] as const
5
+
5
6
  export const useExamples = () => useQuery({ queryKey: exampleQueryKey, queryFn: listExamples })
6
7
  export function useCreateExample() {
7
8
  const queryClient = useQueryClient()
@@ -0,0 +1,3 @@
1
+ import { atom } from 'jotai'
2
+
3
+ export const exampleMessageAtom = atom('')
@@ -1,4 +1,4 @@
1
1
  import { createFileRoute } from '@tanstack/react-router'
2
- import { ExamplePage } from '#/modules/example/ExamplePage'
2
+ import { ExamplePage } from '#/modules/example/components/ExamplePage'
3
3
 
4
4
  export const Route = createFileRoute('/')({ component: ExamplePage })
@@ -5,8 +5,9 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "oyo env server -- vp dev",
8
+ "dev:chain": "turbo watch dev --filter=@repo/stack",
8
9
  "generate-routes": "tsr generate",
9
- "build": "oyo env server -- vp build",
10
+ "build": "vp build",
10
11
  "preview": "oyo env server -- vp preview",
11
12
  "test": "vp test",
12
13
  "check": "vp check"
@@ -1,10 +1,12 @@
1
1
  {
2
- "extends": "@oyolincc/dev-config/tsconfig.app.json",
2
+ "extends": "@oyolincc/dev-config/tsconfig.frontend.json",
3
3
  "compilerOptions": {
4
- "baseUrl": ".",
4
+ "jsx": "react-jsx",
5
5
  "paths": {
6
- "#/*": ["./frontend/*"]
6
+ "#/*": ["./frontend/*"],
7
+ "@/*": ["./nitro/*"]
7
8
  }
8
9
  },
9
- "include": ["frontend", "nitro", "vite.config.ts"]
10
+ "files": [],
11
+ "include": ["frontend", "nitro", "./*.ts", "./*.mts"]
10
12
  }
@@ -1,5 +1,5 @@
1
1
  {
2
+ "target": "react",
2
3
  "routesDirectory": "./frontend/routes",
3
- "generatedRouteTree": "./frontend/routeTree.gen.ts",
4
- "quoteStyle": "single"
4
+ "generatedRouteTree": "./frontend/routeTree.gen.ts"
5
5
  }
@@ -1,2 +1 @@
1
- DB_PATH=./db-data/repo.sqlite
2
-
1
+ DB_PATH=/absolute/path/to/repo.sqlite
@@ -16,6 +16,7 @@
16
16
  "dev": "vp pack --watch",
17
17
  "test": "vp test",
18
18
  "check": "vp check",
19
+ "sqlite:export": "oyo env sqlite -- drizzle-kit export --config=drizzle-kit/sqlite.config.ts",
19
20
  "dev:sqlite:push": "oyo env sqlite -- drizzle-kit push --config=drizzle-kit/sqlite.config.ts",
20
21
  "dev:sqlite:gen": "oyo env sqlite -- drizzle-kit generate --config=drizzle-kit/sqlite.config.ts",
21
22
  "dev:sqlite:migrate": "oyo env sqlite -- drizzle-kit migrate --config=drizzle-kit/sqlite.config.ts"
@@ -6,10 +6,7 @@
6
6
  "prepare": "vp config",
7
7
  "changeset": "vp exec changeset",
8
8
  "build": "turbo build",
9
- "dev": "turbo build --filter=@repo/definition --filter=@repo/db --filter=@repo/server && concurrently -k -n definition,db,server,stack -c blue,magenta,green,yellow \"pnpm --filter @repo/definition dev\" \"pnpm --filter @repo/db dev\" \"pnpm --filter @repo/server dev:pack\" \"pnpm --filter @repo/stack dev\"",
10
- "dev:sqlite:push": "pnpm --filter @repo/db dev:sqlite:push",
11
- "dev:sqlite:gen": "pnpm --filter @repo/db dev:sqlite:gen",
12
- "dev:sqlite:migrate": "pnpm --filter @repo/db dev:sqlite:migrate"
9
+ "build:libs": "turbo build --filter=!@repo/stack"
13
10
  },
14
11
  "devDependencies": {
15
12
  "@changesets/changelog-github": "catalog:",
@@ -17,7 +14,6 @@
17
14
  "@oyolincc/cli": "catalog:",
18
15
  "@oyolincc/dev-config": "catalog:",
19
16
  "@types/node": "catalog:",
20
- "concurrently": "catalog:",
21
17
  "tsx": "catalog:",
22
18
  "turbo": "catalog:",
23
19
  "vite-plus": "catalog:"
@@ -40,7 +40,7 @@ catalogs:
40
40
  '@hono/vite-dev-server': '^0.26.1'
41
41
  '@valibot/to-json-schema': '^1.7.1'
42
42
  'hono': '^4.13.1'
43
- 'hono-openapi': '^1.3.1'
43
+ 'hono-openapi': '^1.3.3'
44
44
  'hono-pino': '^0.10.3'
45
45
  'hono-problem-details': '^0.11.0'
46
46
  'pino': '^10.3.1'
@@ -6,6 +6,7 @@
6
6
  "outputs": ["dist/**", ".output/**"]
7
7
  },
8
8
  "dev": {
9
+ "dependsOn": ["^build"],
9
10
  "cache": false,
10
11
  "persistent": true
11
12
  }
@@ -12,8 +12,7 @@
12
12
  },
13
13
  "scripts": {
14
14
  "dev": "concurrently -k -n dev,pack -c blue,green \"vp dev\" \"VITE_PUBLIC_DEV_SERVE=1 vp pack --watch\"",
15
- "dev:pack": "VITE_PUBLIC_DEV_SERVE=1 vp pack --watch",
16
- "start": "oyo env server -- node dist/nodeMain.mjs",
15
+ "dev:chain": "turbo watch dev --filter=@repo/server",
17
16
  "build": "vp pack",
18
17
  "test": "vp test",
19
18
  "check": "vp check",
@@ -32,9 +32,11 @@ installProcessErrorHandlers(runtime.isLocal)
32
32
  export const exampleWorkRunner = ExampleWorkRunner.getInstance(appConfig.worker)
33
33
 
34
34
  let dependencies: AppDependencies | undefined
35
- const app = createApp(() => {
36
- dependencies ||= { appConfig, loggerHandler, exampleWorkRunner }
37
- return dependencies
35
+ const app = createApp({
36
+ makeDependencies: () => {
37
+ dependencies ||= { appConfig, loggerHandler, exampleWorkRunner }
38
+ return dependencies
39
+ },
38
40
  })
39
41
 
40
42
  export default app
@@ -1,20 +1,18 @@
1
- import { type Context, Hono } from 'hono'
1
+ import { Hono } from 'hono'
2
2
  import { contextStorage } from 'hono/context-storage'
3
3
  import { csrf } from 'hono/csrf'
4
4
  import { secureHeaders } from 'hono/secure-headers'
5
5
  import { requestId } from 'hono/request-id'
6
6
  import { corsMiddleware } from './middlewares/cors'
7
7
  import { exampleRoutes } from './modules/example/routes'
8
- import type { AppDependencies, AppEnv } from './shared/app/types'
8
+ import type { CreateAppOptions, AppEnv } from './shared/app/types'
9
9
  import { E } from './shared/errors/defs'
10
10
  import { globalErrorHandler } from './shared/errors/handler'
11
11
  import { configureOpenAPI } from './shared/network/openapi'
12
12
  import { objectEntries } from './shared/utils'
13
13
 
14
- export const createApp = (
15
- makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies,
16
- ): Hono<AppEnv> => {
17
- const app = new Hono<AppEnv>()
14
+ export const createApp = ({ basePath, makeDependencies }: CreateAppOptions): Hono<AppEnv> => {
15
+ const app = new Hono<AppEnv>().basePath(basePath || '/')
18
16
 
19
17
  app.use('*', async (c, next) => {
20
18
  const deps = await makeDependencies(c)
@@ -8,7 +8,7 @@ import type {
8
8
  WorkerConfig,
9
9
  } from '@repo/definition/backend'
10
10
  import type { AppWorkRunner } from '@/infra/work-runner/types'
11
- import type { MiddlewareHandler } from 'hono'
11
+ import type { MiddlewareHandler, Context } from 'hono'
12
12
  import type { PinoLogger as HonoPinoLogger } from 'hono-pino'
13
13
  import type { RequestIdVariables } from 'hono/request-id'
14
14
 
@@ -38,3 +38,8 @@ export type AppDependencies = {
38
38
  export type AppEnv = {
39
39
  Variables: RequestIdVariables & AppDependencies & { logger?: AppLogger }
40
40
  }
41
+
42
+ export type CreateAppOptions = {
43
+ basePath?: string
44
+ makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies
45
+ }
@@ -1,6 +1,6 @@
1
- import type { ValidationTargets } from 'hono'
1
+ import type { Env, MiddlewareHandler, ValidationTargets } from 'hono'
2
2
  import { validator } from 'hono-openapi'
3
- import { flatten, type GenericSchema } from 'valibot'
3
+ import { flatten, type GenericSchema, type InferInput, type InferOutput } from 'valibot'
4
4
  import { E } from '@/shared/errors/defs'
5
5
 
6
6
  export const generalValidator = <
@@ -9,7 +9,14 @@ export const generalValidator = <
9
9
  >(
10
10
  target: TTarget,
11
11
  schema: TSchema,
12
- ) =>
12
+ ): MiddlewareHandler<
13
+ Env,
14
+ string,
15
+ {
16
+ in: { [K in TTarget]: InferInput<TSchema> }
17
+ out: { [K in TTarget]: InferOutput<TSchema> }
18
+ }
19
+ > =>
13
20
  validator(target, schema, (result, _c) => {
14
21
  if (!result.success) {
15
22
  throw E.NORMAL.VALIDATION.create({
@@ -7,6 +7,6 @@ export default defineConfig({
7
7
  entry: {
8
8
  backend: 'src/backend.ts',
9
9
  },
10
- exports: true,
10
+ exports: false,
11
11
  },
12
12
  })
@@ -36,7 +36,7 @@ catalogs:
36
36
  '@hono/vite-dev-server': '^0.26.1'
37
37
  '@valibot/to-json-schema': '^1.7.1'
38
38
  'hono': '^4.13.1'
39
- 'hono-openapi': '^1.3.1'
39
+ 'hono-openapi': '^1.3.3'
40
40
  'hono-pino': '^0.10.3'
41
41
  'hono-problem-details': '^0.11.0'
42
42
  'pino': '^10.3.1'
@@ -6,6 +6,7 @@
6
6
  "outputs": ["dist/**", ".output/**"]
7
7
  },
8
8
  "dev": {
9
+ "dependsOn": ["^build"],
9
10
  "cache": false,
10
11
  "persistent": true
11
12
  }
@@ -6,6 +6,7 @@
6
6
  "outputs": ["dist/**", ".output/**"]
7
7
  },
8
8
  "dev": {
9
+ "dependsOn": ["^build"],
9
10
  "cache": false,
10
11
  "persistent": true
11
12
  }
@@ -1,52 +0,0 @@
1
- import { describe, expect, it } from 'vite-plus/test'
2
- import { createApp } from './app'
3
- import type { AppDependencies, AppLogger } from './shared/app/types'
4
-
5
- const logger = new Proxy(
6
- { assign() {} },
7
- { get: (target, key) => Reflect.get(target, key) || (() => {}) },
8
- ) as AppLogger
9
- const dependencies = {
10
- appConfig: { middleware: { cors: { domains: ['*'] } } },
11
- loggerHandler: async (c: any, next: () => Promise<void>) => {
12
- c.set('logger', logger)
13
- await next()
14
- },
15
- exampleWorkRunner: {
16
- async run(task: any) {
17
- const original = task.input.message
18
- const normalized = original.trim().replace(/\s+/g, ' ')
19
- return { ok: true as const, data: { original, normalized, length: normalized.length } }
20
- },
21
- async shutdown() {},
22
- },
23
- } as unknown as AppDependencies
24
-
25
- describe('HTTP contracts', () => {
26
- const app = createApp(() => dependencies)
27
-
28
- it('wraps successful responses in data', async () => {
29
- const response = await app.request('/health')
30
- expect(response.status).toBe(200)
31
- await expect(response.json()).resolves.toEqual({ data: { status: 'ok' } })
32
- })
33
-
34
- it('returns Problem Details for missing routes', async () => {
35
- const response = await app.request('/missing')
36
- expect(response.status).toBe(404)
37
- await expect(response.json()).resolves.toMatchObject({ status: 404, type: 'NORMAL.NOT_FOUND' })
38
- })
39
-
40
- it('returns validation Problem Details with status 422', async () => {
41
- const response = await app.request('/examples/process', {
42
- method: 'POST',
43
- headers: { 'content-type': 'application/json' },
44
- body: JSON.stringify({ message: '' }),
45
- })
46
- expect(response.status).toBe(422)
47
- await expect(response.json()).resolves.toMatchObject({
48
- status: 422,
49
- type: 'NORMAL.VALIDATION',
50
- })
51
- })
52
- })
@@ -1,52 +0,0 @@
1
- import { describe, expect, it } from 'vite-plus/test'
2
- import { createApp } from './app'
3
- import type { AppDependencies, AppLogger } from './shared/app/types'
4
-
5
- const logger = new Proxy(
6
- { assign() {} },
7
- { get: (target, key) => Reflect.get(target, key) || (() => {}) },
8
- ) as AppLogger
9
- const dependencies = {
10
- appConfig: { middleware: { cors: { domains: ['*'] } } },
11
- loggerHandler: async (c: any, next: () => Promise<void>) => {
12
- c.set('logger', logger)
13
- await next()
14
- },
15
- exampleWorkRunner: {
16
- async run(task: any) {
17
- const original = task.input.message
18
- const normalized = original.trim().replace(/\s+/g, ' ')
19
- return { ok: true as const, data: { original, normalized, length: normalized.length } }
20
- },
21
- async shutdown() {},
22
- },
23
- } as unknown as AppDependencies
24
-
25
- describe('HTTP contracts', () => {
26
- const app = createApp(() => dependencies)
27
-
28
- it('wraps successful responses in data', async () => {
29
- const response = await app.request('/health')
30
- expect(response.status).toBe(200)
31
- await expect(response.json()).resolves.toEqual({ data: { status: 'ok' } })
32
- })
33
-
34
- it('returns Problem Details for missing routes', async () => {
35
- const response = await app.request('/missing')
36
- expect(response.status).toBe(404)
37
- await expect(response.json()).resolves.toMatchObject({ status: 404, type: 'NORMAL.NOT_FOUND' })
38
- })
39
-
40
- it('returns validation Problem Details with status 422', async () => {
41
- const response = await app.request('/examples/process', {
42
- method: 'POST',
43
- headers: { 'content-type': 'application/json' },
44
- body: JSON.stringify({ message: '' }),
45
- })
46
- expect(response.status).toBe(422)
47
- await expect(response.json()).resolves.toMatchObject({
48
- status: 422,
49
- type: 'NORMAL.VALIDATION',
50
- })
51
- })
52
- })