Skip to content

Commit 1d50db2

Browse files
authored
refactor: use modern things (#29)
* refactor: use modern things * refactor package.json * remove showRoutes
1 parent 4aa5c49 commit 1d50db2

26 files changed

Lines changed: 7485 additions & 4323 deletions

jest.config.js

Lines changed: 0 additions & 17 deletions
This file was deleted.

mock/authors/yusukebe/info.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

mock/shops.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

mock/shops/sugitaya/info.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

mock/shops/takasagoya/info.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

mock/shops/yoshimuraya/info.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

mock/shops/yoshimuraya/yoshimuraya-001.jpg

Whitespace-only changes.

package.json

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,35 @@
44
"main": "src/index.ts",
55
"type": "module",
66
"scripts": {
7-
"test": "jest --verbose",
8-
"validate": "jest ./validation/* --verbose",
9-
"dev": "wrangler dev --live-reload src/index.ts",
10-
"deploy": "wrangler deploy --minify src/index.ts",
11-
"tail": "wrangler tail"
7+
"test": "vitest",
8+
"validate": "vitest run --config vitest.validation.config.ts",
9+
"dev": "wrangler dev",
10+
"deploy": "wrangler deploy --minify",
11+
"tail": "wrangler tail",
12+
"cf-typegen": "wrangler types"
1213
},
1314
"license": "MIT",
1415
"dependencies": {
1516
"@hono/graphql-server": "^0.4.1",
16-
"@modelcontextprotocol/sdk": "^1.11.4",
17+
"@modelcontextprotocol/sdk": "^1.12.1",
1718
"fetch-to-node": "^2.1.0",
1819
"graphql": "^16.6.0",
19-
"hono": "^4.7.10",
20+
"hono": "^4.7.11",
2021
"zod": "^3.25.7"
2122
},
2223
"devDependencies": {
23-
"@cloudflare/workers-types": "^4.20231218.0",
24+
"@cloudflare/vitest-pool-workers": "^0.8.36",
2425
"@hono/eslint-config": "^0.0.3",
25-
"@jest/expect": "^29.1.2",
26-
"@types/jest": "^29.1.2",
27-
"esbuild": "^0.15.10",
28-
"esbuild-jest": "^0.5.0",
26+
"@types/node": "^22.15.30",
2927
"eslint": "^8.56.0",
30-
"jest": "^29.1.2",
31-
"jest-environment-miniflare": "^2.14.1",
32-
"miniflare": "^2.10.0",
3328
"typescript": "^5.3.3",
34-
"wrangler": "3"
29+
"vitest": "~3.1.0",
30+
"wrangler": "^4.19.1"
3531
},
3632
"engines": {
3733
"node": ">=18"
34+
},
35+
"resolutions": {
36+
"graphql": "^16.6.0"
3837
}
39-
}
38+
}

src/app.ts

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
import type { KVNamespace } from '@cloudflare/workers-types'
21
import type { Context } from 'hono'
3-
import { getContentFromKVAsset } from './workers-utils'
2+
import { getMimeType } from 'hono/utils/mime'
3+
import {
4+
getShopsData,
5+
getShopInfo,
6+
getAuthorInfo,
7+
getShopImageResponse,
8+
} from './data'
49

510
export const BASE_URL = 'http://localhost/'
611

@@ -9,7 +14,9 @@ export type Env = {
914
BASE_URL: string
1015
}
1116
Bindings: {
12-
__STATIC_CONTENT: KVNamespace
17+
ASSETS?: Fetcher
18+
__STATIC_CONTENT?: KVNamespace
19+
MCP_OBJECT?: DurableObjectNamespace
1320
}
1421
}
1522

@@ -52,7 +59,7 @@ type listShopsResult = {
5259
totalCount: number
5360
}
5461

55-
type listShopsWithPagerResult = {
62+
export type listShopsWithPagerResult = {
5663
shops: Shop[]
5764
totalCount: number
5865
pageInfo: pageInfo
@@ -106,11 +113,7 @@ export const listShops = async (
106113
options: Options
107114
): Promise<listShopsResult> => {
108115
const { limit = 10, offset = 0 } = params
109-
const c = options.c
110-
const buffer = await getContentFromKVAsset('shops.json', {
111-
namespace: c.env ? c.env.__STATIC_CONTENT : undefined,
112-
})
113-
const data = arrayBufferToJSON(buffer)
116+
const data = await getShopsData(options.c.env.ASSETS, options.c.req.url)
114117

115118
const shopIdsAll = data['shopIds']
116119
const totalCount = shopIdsAll.length
@@ -147,29 +150,31 @@ export const findIndexFromId = async (
147150
export const getShop = async (id: string, options: Options): Promise<Shop> => {
148151
let shop: Shop
149152
try {
150-
const c = options.c
151-
const buffer = await getContentFromKVAsset(`shops/${id}/info.json`, {
152-
namespace: c.env ? c.env.__STATIC_CONTENT : undefined,
153-
})
154-
shop = arrayBufferToJSON(buffer)
155-
} catch (e) {
156-
throw new Error(`"shops/${id}/info.json" is not found: ${e}`)
157-
}
153+
shop = (await getShopInfo(
154+
id,
155+
options.c.env.ASSETS,
156+
options.c.req.url
157+
)) as Shop
158+
} catch {} // Do nothing
158159
if (!shop) return
159160
shop.photos?.map((photo: Photo) => {
160161
photo.url = fixPhotoURL({ shopId: id, path: photo.name }, options)
161162
})
162163
return shop
163164
}
164165

165-
export const getAuthor = async (id: string): Promise<Author> => {
166+
export const getAuthor = async (
167+
id: string,
168+
options?: Options
169+
): Promise<Author> => {
166170
let author: Author
167171
try {
168-
const buffer = await getContentFromKVAsset(`authors/${id}/info.json`)
169-
author = arrayBufferToJSON(buffer)
170-
} catch (e) {
171-
throw new Error(`"authors/${id}/info.json" is not found: ${e}`)
172-
}
172+
author = (await getAuthorInfo(
173+
id,
174+
options?.c.env?.ASSETS,
175+
options?.c.req?.url
176+
)) as Author
177+
} catch {} // Do nothing
173178
if (!author) return
174179
return author
175180
}
@@ -188,45 +193,35 @@ const fixPhotoURL = (
188193
return `${options.c.var.BASE_URL}images/${shopId}/${path}`
189194
}
190195

191-
const arrayBufferToJSON = (arrayBuffer: ArrayBuffer) => {
192-
if (arrayBuffer instanceof ArrayBuffer) {
193-
const text = new TextDecoder().decode(arrayBuffer)
194-
if (text) return JSON.parse(text)
195-
} else {
196-
return arrayBuffer
197-
}
198-
}
199-
200196
export const getShopPhotosWithData = async (
201197
shopId: string,
202198
options: Options
203199
): Promise<PhotoWithData[]> => {
204200
const shop = await getShop(shopId, options)
205-
206-
const photos = await Promise.all(
207-
shop.photos?.map(async (photo): Promise<PhotoWithData | null> => {
208-
try {
209-
const buffer = await getContentFromKVAsset(
210-
`shops/${shopId}/${photo.name}`,
211-
{
212-
namespace: options.c.env
213-
? options.c.env.__STATIC_CONTENT
214-
: undefined,
215-
}
216-
)
217-
const base64 = arrayBufferToBase64(buffer)
218-
return {
201+
if (!shop || !shop.photos) return []
202+
203+
const photos: PhotoWithData[] = []
204+
205+
for (const photo of shop.photos) {
206+
try {
207+
const response = await getShopImageResponse(
208+
shopId,
209+
photo.name,
210+
options.c.env.ASSETS,
211+
options.c.var.BASE_URL
212+
)
213+
if (response.ok) {
214+
const arrayBuffer = await response.arrayBuffer()
215+
const base64 = arrayBufferToBase64(arrayBuffer)
216+
photos.push({
219217
...photo,
220218
base64,
221-
}
222-
} catch (e) {
223-
console.error(`Failed to load image ${photo.name}:`, e)
224-
return null
219+
})
225220
}
226-
}) || []
227-
)
221+
} catch {} // Do nothing
222+
}
228223

229-
return photos.filter(Boolean)
224+
return photos
230225
}
231226

232227
export const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer): string => {
@@ -237,3 +232,24 @@ export const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer): string => {
237232
}
238233
return btoa(binary)
239234
}
235+
236+
export const getShopImage = async (
237+
shopId: string,
238+
filename: string,
239+
assets: Fetcher,
240+
baseUrl: string
241+
): Promise<Response> => {
242+
const mimeType = getMimeType(filename)
243+
const response = await getShopImageResponse(shopId, filename, assets, baseUrl)
244+
245+
if (response.ok) {
246+
const content = await response.arrayBuffer()
247+
return new Response(content, {
248+
headers: {
249+
'Content-Type': mimeType || 'application/octet-stream',
250+
},
251+
})
252+
}
253+
254+
return new Response(null, { status: 404 })
255+
}

src/data.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const CONTENT_PREFIX =
2+
process.env && process.env.NODE_ENV === 'test' ? '/content' : ''
3+
4+
export const getShopsData = async (assets: Fetcher, baseUrl: string) => {
5+
const response = await assets.fetch(
6+
new URL(`${CONTENT_PREFIX}/shops.json`, baseUrl)
7+
)
8+
return await response.json()
9+
}
10+
11+
export const getShopInfo = async (
12+
shopId: string,
13+
assets: Fetcher,
14+
baseUrl: string
15+
) => {
16+
const response = await assets.fetch(
17+
new URL(`${CONTENT_PREFIX}/shops/${shopId}/info.json`, baseUrl)
18+
)
19+
return await response.json()
20+
}
21+
22+
export const getAuthorInfo = async (
23+
authorId: string,
24+
assets: Fetcher,
25+
baseUrl: string
26+
) => {
27+
const response = await assets.fetch(
28+
new URL(`${CONTENT_PREFIX}/authors/${authorId}/info.json`, baseUrl)
29+
)
30+
return await response.json()
31+
}
32+
33+
export const getShopImageResponse = async (
34+
shopId: string,
35+
filename: string,
36+
assets: Fetcher,
37+
baseUrl: string
38+
) => {
39+
const path = `${CONTENT_PREFIX}/shops/${shopId}/${filename}`
40+
return await assets.fetch(new URL(path, baseUrl))
41+
}

0 commit comments

Comments
 (0)