Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This is the main way people contribute. Add one shop end to end.
3. **photo** — path to a real image the user provides. Do not invent one.
4. **author** — `authorId` (`^[0-9a-zA-Z\-\_]+$`), display `name`, profile `url` (`^https?://.+`). Reuse an existing `content/authors/{authorId}` if present.
5. **prefecture** — optional; see step 4.
6. **location** — optional WGS 84 decimal-degree coordinates; see step 4.

### Steps

Expand All @@ -39,13 +40,19 @@ This is the main way people contribute. Add one shop end to end.

4. **Prefecture** (optional but encouraged) — the 都道府県 the shop is physically in (must end with 都/道/府/県). **Verify the real location** (search the web); do not infer from the name — a "札幌ラーメン" shop can be in Kanagawa, a 家系 shop can be in Tokyo. For a nationwide chain or any shop whose branch you cannot pin down, **omit the field entirely** rather than recording a guess.

5. **Write `content/shops/{shopId}/info.json`** — field order `id`, `name`, `prefecture` (if known), `photos`. Do **not** add a `url` field (it is generated at runtime).
If the branch is confirmed, add `location` as `{ "lat": number, "lng": number }`; otherwise omit it.

5. **Write `content/shops/{shopId}/info.json`** — field order `id`, `name`, `prefecture` (if known), `location` (if known), `photos`. Do **not** add a `url` field (it is generated at runtime).

```json
{
"id": "{shopId}",
"name": "{name}",
"prefecture": "{prefecture}",
"location": {
"lat": 35.463062,
"lng": 139.615063
},
"photos": [
{
"name": "{shopId}-001.jpg",
Expand All @@ -68,13 +75,16 @@ This is the main way people contribute. Add one shop end to end.
| `photos[].width` / `height` | numbers (the real pixel size) |
| `photos[].authorId` | `^[0-9a-zA-Z\-\_]+$`, must resolve to an author |
| `prefecture` | optional; if present must end with 都/道/府/県 |
| `location.lat` | optional; number between -90 and 90 |
| `location.lng` | optional; number between -180 and 180 |

### Conventions / gotchas

- Never store a photo `url`; it is generated by the API at runtime.
- Do not create or edit `content/shops.json`; it is generated from the `content/shops/*/` directories (`vp run generate`) and is not committed. Do not edit `tools/legacy-shops-order.json` either; it only freezes the order of the shops added before.
- Keep photos under 300KB, longest side ~800px, EXIF stripped.
- Omit `prefecture` when uncertain; never fabricate a location.
- Omit `location` when the shop branch cannot be identified; never fabricate coordinates.

## Optional: local checks

Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type Shop = {
id: string
name?: string
prefecture?: string
location?: {
lat: number
lng: number
}
photos?: Photo[]
}

Expand Down Expand Up @@ -99,6 +103,10 @@ GET /shops?pretty&prefecture=神奈川県
"id": "yoshimuraya",
"name": "吉村家",
"prefecture": "神奈川県",
"location": {
"lat": 35.463062,
"lng": 139.615063
},
"photos": [
{
"name": "yoshimuraya-001.jpg",
Expand Down Expand Up @@ -233,8 +241,14 @@ type Shop {
id: String
name: String
prefecture: String
location: Location
photos: [Photo]
}

type Location {
lat: Float
lng: Float
}
```

#### Photo
Expand Down Expand Up @@ -387,6 +401,7 @@ Edit `./content/shops/{shopId}/info.json` like this:
"id": "yoshimuraya", // <-- must be /^[0-9a-z\-]+$/
"name": "吉村家",
"prefecture": "神奈川県", // <-- optional. a prefecture name ending with 都道府県
"location": { "lat": 35.463062, "lng": 139.615063 }, // <-- optional; WGS 84 decimal degrees
"photos": [
{
"name": "yoshimuraya-001.jpg", // <-- must be /^[0-9a-z\-\.]+\.(jpg|jpeg|png|gif)$/
Expand Down
4 changes: 4 additions & 0 deletions content/shops/yoshimuraya/info.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"id": "yoshimuraya",
"name": "吉村家",
"prefecture": "神奈川県",
"location": {
"lat": 35.463062,
"lng": 139.615063
},
"photos": [
{
"name": "yoshimuraya-001.jpg",
Expand Down
4 changes: 4 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export type Shop = {
id: string
name?: string
prefecture?: string
location?: {
lat: number
lng: number
}
photos?: Photo[]
}

Expand Down
10 changes: 10 additions & 0 deletions src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
GraphQLSchema,
GraphQLList,
GraphQLInt,
GraphQLFloat,
} from 'graphql'
import { getShop, listShops, getAuthor, type Shop, type Options } from '@/app'
import { Pager } from '@/pager'
Expand Down Expand Up @@ -35,6 +36,15 @@ const shopType = new GraphQLObjectType({
id: { type: GraphQLString },
name: { type: GraphQLString },
prefecture: { type: GraphQLString },
location: {
type: new GraphQLObjectType({
name: 'Location',
fields: {
lat: { type: GraphQLFloat },
lng: { type: GraphQLFloat },
},
}),
},
photos: {
type: new GraphQLList(photoType),
},
Expand Down
10 changes: 10 additions & 0 deletions validation/shops.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ describe('Validate shops with SELF and REST API', () => {
expect(shop.prefecture).toMatch(/^.+[都道府県]$/)
}

// location is optional, but when present it must contain valid coordinates
if (shop.location !== undefined) {
expect(typeof shop.location.lat).toBe('number')
expect(typeof shop.location.lng).toBe('number')
expect(shop.location.lat).toBeGreaterThanOrEqual(-90)
expect(shop.location.lat).toBeLessThanOrEqual(90)
expect(shop.location.lng).toBeGreaterThanOrEqual(-180)
expect(shop.location.lng).toBeLessThanOrEqual(180)
}

// Validate each photo
for (const photo of shop.photos) {
expect(photo.name).toMatch(/^[0-9a-z.-]+\.(jpg|jpeg|png|gif)$/)
Expand Down