@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.
- package/package.json +1 -1
- package/templates/hono-fullstack/apps/server/package.json +1 -2
- package/templates/hono-fullstack/apps/server/src/adapters/nodejs/entry.ts +6 -3
- package/templates/hono-fullstack/apps/server/src/app.ts +4 -6
- package/templates/hono-fullstack/apps/server/src/shared/app/types.ts +6 -1
- package/templates/hono-fullstack/apps/server/src/shared/network/validator.ts +10 -3
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/{ExamplePage.tsx → components/ExamplePage.tsx} +5 -3
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/{hooks.ts → hooks/api-hooks.ts} +2 -1
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/stores/index.ts +3 -0
- package/templates/hono-fullstack/apps/stack/frontend/routes/index.tsx +1 -1
- package/templates/hono-fullstack/apps/stack/package.json +2 -1
- package/templates/hono-fullstack/apps/stack/tsconfig.json +6 -4
- package/templates/hono-fullstack/apps/stack/tsr.config.json +2 -2
- package/templates/hono-fullstack/envs/.env.example.sqlite +1 -2
- package/templates/hono-fullstack/libs/db/package.json +1 -0
- package/templates/hono-fullstack/package.json +1 -5
- package/templates/hono-fullstack/pnpm-workspace.yaml +1 -1
- package/templates/hono-fullstack/turbo.json +1 -0
- package/templates/hono-server/apps/server/package.json +1 -2
- package/templates/hono-server/apps/server/src/adapters/nodejs/entry.ts +5 -3
- package/templates/hono-server/apps/server/src/app.ts +4 -6
- package/templates/hono-server/apps/server/src/shared/app/types.ts +6 -1
- package/templates/hono-server/apps/server/src/shared/network/validator.ts +10 -3
- package/templates/hono-server/libs/definition/vite.config.ts +1 -1
- package/templates/hono-server/pnpm-workspace.yaml +1 -1
- package/templates/hono-server/turbo.json +1 -0
- package/templates/monorepo/turbo.json +1 -0
- package/templates/hono-fullstack/apps/server/src/app.test.ts +0 -52
- package/templates/hono-server/apps/server/src/app.test.ts +0 -52
package/package.json
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
44
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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] =
|
|
7
|
+
const [message, setMessage] = useAtom(exampleMessageAtom)
|
|
6
8
|
const examples = useExamples()
|
|
7
9
|
const create = useCreateExample()
|
|
8
10
|
const process = useProcessExample()
|
package/templates/hono-fullstack/apps/stack/frontend/modules/example/{hooks.ts → hooks/api-hooks.ts}
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
2
|
-
import { createExample, listExamples, processExample } from '
|
|
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()
|
|
@@ -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": "
|
|
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.
|
|
2
|
+
"extends": "@oyolincc/dev-config/tsconfig.frontend.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
-
"
|
|
4
|
+
"jsx": "react-jsx",
|
|
5
5
|
"paths": {
|
|
6
|
-
"#/*": ["./frontend/*"]
|
|
6
|
+
"#/*": ["./frontend/*"],
|
|
7
|
+
"@/*": ["./nitro/*"]
|
|
7
8
|
}
|
|
8
9
|
},
|
|
9
|
-
"
|
|
10
|
+
"files": [],
|
|
11
|
+
"include": ["frontend", "nitro", "./*.ts", "./*.mts"]
|
|
10
12
|
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
DB_PATH
|
|
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
|
-
"
|
|
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:"
|
|
@@ -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:
|
|
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
|
-
|
|
37
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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,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
|
-
})
|