@roastbook/open-coffee-schema 0.0.0-stage → 0.4.0
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/LICENSE +21 -0
- package/README.md +65 -2
- package/brew-methods/latest.json +312 -0
- package/brew-methods/v0.1.0.json +265 -0
- package/brew-methods/v0.2.0.json +265 -0
- package/brew-methods/v0.3.0.json +272 -0
- package/brew-methods/v0.4.0.json +312 -0
- package/coffee-places/context.latest.json +246 -0
- package/coffee-places/context.v0.1.0.json +241 -0
- package/coffee-places/context.v0.3.0.json +246 -0
- package/coffee-places/context.v0.4.0.json +246 -0
- package/coffee-places/latest.json +577 -0
- package/coffee-places/v0.1.0.json +518 -0
- package/coffee-places/v0.2.0.json +518 -0
- package/coffee-places/v0.3.0.json +577 -0
- package/coffee-places/v0.4.0.json +577 -0
- package/common/latest.json +197 -0
- package/common/v0.1.0.json +192 -0
- package/common/v0.2.0.json +197 -0
- package/common/v0.3.0.json +197 -0
- package/common/v0.4.0.json +197 -0
- package/equipment/latest.json +895 -0
- package/equipment/v0.1.0.json +1122 -0
- package/equipment/v0.2.0.json +1122 -0
- package/equipment/v0.3.0.json +895 -0
- package/equipment/v0.4.0.json +895 -0
- package/index.d.ts +1114 -0
- package/index.js +94 -0
- package/package.json +67 -4
- package/schemas.d.ts +5 -0
- package/schemas.js +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roastbook Open Coffee contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @roastbook/open-coffee-schema
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript types, JSON Schemas, the JSON-LD context and the allowed values of the [Open Coffee](https://schema.roastbook.coffee/) standard: coffee places, equipment and brew methods. By [Roastbook](https://roastbook.coffee/).
|
|
4
|
+
|
|
5
|
+
The package version is the release of the standard (`1.2.0` = tag `v1.2.0` of [Roastbook/open-coffee-schema](https://github.com/Roastbook/open-coffee-schema)). Each family keeps its own schema version inside it; see `SCHEMA_IDS`. No runtime dependencies.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
The package is on the public npm registry. No token or registry setting is needed:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bun add @roastbook/open-coffee-schema # or: npm i @roastbook/open-coffee-schema
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Releases are published from GitHub Actions with npm Trusted Publishing and carry provenance, which links each version to the release workflow run in [Roastbook/open-coffee-schema](https://github.com/Roastbook/open-coffee-schema). Versions 0.1.0 to 0.3.0 were published only to GitHub Packages (`npm.pkg.github.com`); from 0.4.0 the package is on npmjs.org.
|
|
16
|
+
|
|
17
|
+
## Types
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import type { CoffeePlacesDataset, CoffeePlace, BrewMethodsDataset, BrewMethod, EquipmentItem, SpecialtyTier } from "@roastbook/open-coffee-schema";
|
|
21
|
+
|
|
22
|
+
const res = await fetch("https://data.roastbook.coffee/datasets/coffee-places/de/munich.json");
|
|
23
|
+
const munich = (await res.json()) as CoffeePlacesDataset;
|
|
24
|
+
const filterBars: CoffeePlace[] = munich.places.filter((p) => p.serving?.filter);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The types come from the newest version of each family. Rules that TypeScript cannot express (patterns, ranges, "closed_on only when status is closed") are checked by validation, below.
|
|
28
|
+
|
|
29
|
+
## Validation with Ajv
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
33
|
+
import addFormats from "ajv-formats";
|
|
34
|
+
import { schemas } from "@roastbook/open-coffee-schema/schemas";
|
|
35
|
+
import { SCHEMA_IDS, isIri } from "@roastbook/open-coffee-schema";
|
|
36
|
+
|
|
37
|
+
const ajv = new Ajv2020({ allErrors: true });
|
|
38
|
+
addFormats(ajv);
|
|
39
|
+
ajv.addFormat("iri", isIri); // URL fields use the "iri" format, which ajv-formats lacks
|
|
40
|
+
for (const schema of Object.values(schemas)) ajv.addSchema(schema);
|
|
41
|
+
|
|
42
|
+
const validate = ajv.getSchema(SCHEMA_IDS["brew-methods"].latest)!;
|
|
43
|
+
if (!validate(data)) console.error(validate.errors);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Validate against the `$id` a file names in its `$schema` to keep old files valid after new versions come out: `ajv.getSchema(data.$schema)`.
|
|
47
|
+
|
|
48
|
+
Single schema files are exported too: `@roastbook/open-coffee-schema/coffee-places/v0.4.0.json` (every published version stays included, for example `…/coffee-places/v0.1.0.json`), `…/coffee-places/latest.json`, and the JSON-LD context `…/coffee-places/context.v0.4.0.json` (`context.latest.json`; `.json` so Node and bundlers can import it).
|
|
49
|
+
|
|
50
|
+
## Allowed values
|
|
51
|
+
|
|
52
|
+
Every fixed list of values is exported as a read-only array, so apps do not re-declare them:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { SPECIALTY_TIER_VALUES, EQUIPMENT_TYPE_VALUES, BREW_PARAMETER_VALUES, COFFEE_PLACE_STATUS_VALUES } from "@roastbook/open-coffee-schema";
|
|
56
|
+
|
|
57
|
+
for (const type of EQUIPMENT_TYPE_VALUES) console.log(type); // "espresso_machine", "grinder", …
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Names follow the type (`SpecialtyTier` → `SPECIALTY_TIER_VALUES`) or the family and field path (`status` of a coffee place → `COFFEE_PLACE_STATUS_VALUES`).
|
|
61
|
+
|
|
62
|
+
Coffee journals can take the values of recorded brew parameters from here too: `BREW_PARAMETER_VALUES` (the parameter keys), `PAPER_FILTER_POSITION_VALUES` and `DISTRIBUTION_METHOD_VALUES`. A journal that keeps one ratio plus a basis maps a ratio based on brew water to `brew_ratio` and a ratio based on the yield to `yield_ratio`.
|
|
63
|
+
|
|
64
|
+
## Licence
|
|
65
|
+
|
|
66
|
+
MIT. Field documentation: <https://schema.roastbook.coffee/>.
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schema.roastbook.coffee/brew-methods/brew-methods.schema.v0.4.0.json",
|
|
4
|
+
"title": "Open Coffee brew methods",
|
|
5
|
+
"description": "Ways of brewing coffee (espresso, pour-over, V60, AeroPress, cold brew…), their typical parameters and what a coffee journal should record for each. Methods form a tree: a device-specific method such as v60 has the general method pour-over as parent. Part of Roastbook Open Coffee.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"$schema",
|
|
9
|
+
"meta",
|
|
10
|
+
"brew_methods"
|
|
11
|
+
],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"$schema": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^https://schema\\.roastbook\\.coffee/brew-methods/brew-methods\\.schema\\.v[0-9]+\\.[0-9]+\\.[0-9]+\\.json$",
|
|
17
|
+
"description": "Canonical URL of the schema version this file follows."
|
|
18
|
+
},
|
|
19
|
+
"meta": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/dataset_meta",
|
|
22
|
+
"unevaluatedProperties": false
|
|
23
|
+
},
|
|
24
|
+
"brew_methods": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"$ref": "#/$defs/brew_method"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"$defs": {
|
|
32
|
+
"range": {
|
|
33
|
+
"title": "Range",
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"minProperties": 1,
|
|
37
|
+
"properties": {
|
|
38
|
+
"min": {
|
|
39
|
+
"type": "number",
|
|
40
|
+
"minimum": 0,
|
|
41
|
+
"description": "Lowest common value, in the unit named by the parameter (for example grams for dose_g, degrees Celsius for brew_temperature_celsius)."
|
|
42
|
+
},
|
|
43
|
+
"max": {
|
|
44
|
+
"type": "number",
|
|
45
|
+
"minimum": 0,
|
|
46
|
+
"description": "Highest common value, in the unit named by the parameter."
|
|
47
|
+
},
|
|
48
|
+
"typical": {
|
|
49
|
+
"type": "number",
|
|
50
|
+
"minimum": 0,
|
|
51
|
+
"description": "A good starting value, in the unit named by the parameter."
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"description": "A starting range. When several values are given they must be ordered: min ≤ typical ≤ max. JSON Schema cannot compare two values, so this repository checks it in its examples and data repositories check it in their data."
|
|
55
|
+
},
|
|
56
|
+
"parameter": {
|
|
57
|
+
"title": "Brew parameter",
|
|
58
|
+
"enum": [
|
|
59
|
+
"dose_g",
|
|
60
|
+
"brew_water_g",
|
|
61
|
+
"yield_g",
|
|
62
|
+
"brew_ratio",
|
|
63
|
+
"yield_ratio",
|
|
64
|
+
"grind_setting",
|
|
65
|
+
"brew_time_seconds",
|
|
66
|
+
"target_time_seconds",
|
|
67
|
+
"brew_temperature_celsius",
|
|
68
|
+
"bloom_time_seconds",
|
|
69
|
+
"bloom_water_g",
|
|
70
|
+
"preinfusion_time_seconds",
|
|
71
|
+
"preinfusion_pressure_bar",
|
|
72
|
+
"brew_pressure_bar",
|
|
73
|
+
"flow_rate_ml_per_second",
|
|
74
|
+
"tamp_force_kg",
|
|
75
|
+
"uses_puck_screen",
|
|
76
|
+
"paper_filter_position",
|
|
77
|
+
"distribution_method",
|
|
78
|
+
"pour_count",
|
|
79
|
+
"steep_time_seconds"
|
|
80
|
+
],
|
|
81
|
+
"description": "Parameter keys shared with coffee journals such as Roastbook. brew_ratio is grams of brew water per gram of coffee (16 means 1:16); yield_ratio is grams of drink in the cup per gram of coffee (2 means a 1:2 espresso). brew_temperature_celsius is the temperature of the brew water. uses_puck_screen records whether a puck screen sat on top of the espresso puck (true or false). paper_filter_position takes a paper_filter_position value and distribution_method a distribution_method value."
|
|
82
|
+
},
|
|
83
|
+
"grind_size": {
|
|
84
|
+
"title": "Grind size",
|
|
85
|
+
"enum": [
|
|
86
|
+
"extra_fine",
|
|
87
|
+
"fine",
|
|
88
|
+
"medium_fine",
|
|
89
|
+
"medium",
|
|
90
|
+
"medium_coarse",
|
|
91
|
+
"coarse",
|
|
92
|
+
"extra_coarse"
|
|
93
|
+
],
|
|
94
|
+
"description": "Descriptive grind size; extra_fine is Turkish, fine espresso, medium filter, coarse French press and cold brew."
|
|
95
|
+
},
|
|
96
|
+
"paper_filter_position": {
|
|
97
|
+
"title": "Paper filter position",
|
|
98
|
+
"enum": [
|
|
99
|
+
"none",
|
|
100
|
+
"top",
|
|
101
|
+
"bottom",
|
|
102
|
+
"both"
|
|
103
|
+
],
|
|
104
|
+
"description": "Where paper filter discs sit in an espresso basket. none: no paper filter; top: on top of the coffee, under the shower screen; bottom: under the coffee, at the basket floor; both: one disc on top and one at the bottom."
|
|
105
|
+
},
|
|
106
|
+
"distribution_method": {
|
|
107
|
+
"title": "Distribution method",
|
|
108
|
+
"enum": [
|
|
109
|
+
"wdt",
|
|
110
|
+
"blind_shaker",
|
|
111
|
+
"distribution_tool",
|
|
112
|
+
"stockfleth_move"
|
|
113
|
+
],
|
|
114
|
+
"description": "How ground coffee is spread evenly in the basket before tamping. wdt: stirring with fine needles (Weiss Distribution Technique); blind_shaker: grinding into a closed cup, shaking it and tipping the coffee into the basket; distribution_tool: a spinning leveller set on the basket; stockfleth_move: sweeping the coffee level with a finger while turning the portafilter."
|
|
115
|
+
},
|
|
116
|
+
"brew_method": {
|
|
117
|
+
"title": "Brew method",
|
|
118
|
+
"type": "object",
|
|
119
|
+
"additionalProperties": false,
|
|
120
|
+
"required": [
|
|
121
|
+
"id",
|
|
122
|
+
"name",
|
|
123
|
+
"category",
|
|
124
|
+
"extraction",
|
|
125
|
+
"description",
|
|
126
|
+
"checked_at"
|
|
127
|
+
],
|
|
128
|
+
"properties": {
|
|
129
|
+
"id": {
|
|
130
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/id",
|
|
131
|
+
"description": "Stable readable slug, e.g. v60, french-press. Never reused.",
|
|
132
|
+
"examples": [
|
|
133
|
+
"v60"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"name": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"minLength": 1,
|
|
139
|
+
"description": "Common English name of the method, for example Pour-over or French press."
|
|
140
|
+
},
|
|
141
|
+
"aliases": {
|
|
142
|
+
"type": "array",
|
|
143
|
+
"uniqueItems": true,
|
|
144
|
+
"items": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"minLength": 1
|
|
147
|
+
},
|
|
148
|
+
"description": "Other names in any language, e.g. Filterkaffee, drip."
|
|
149
|
+
},
|
|
150
|
+
"parent": {
|
|
151
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/id",
|
|
152
|
+
"description": "The more general method, e.g. pour-over for v60."
|
|
153
|
+
},
|
|
154
|
+
"category": {
|
|
155
|
+
"enum": [
|
|
156
|
+
"espresso",
|
|
157
|
+
"filter",
|
|
158
|
+
"immersion",
|
|
159
|
+
"pressure",
|
|
160
|
+
"vacuum",
|
|
161
|
+
"boiling",
|
|
162
|
+
"cold"
|
|
163
|
+
],
|
|
164
|
+
"description": "Family used for grouping: espresso (9-bar style pressure), filter (percolation through a filter), immersion (steep, then separate), pressure (non-espresso pressure such as AeroPress or moka pot), vacuum (siphon), boiling (cezve/ibrik), cold (cold water over hours)."
|
|
165
|
+
},
|
|
166
|
+
"extraction": {
|
|
167
|
+
"enum": [
|
|
168
|
+
"percolation",
|
|
169
|
+
"immersion",
|
|
170
|
+
"hybrid"
|
|
171
|
+
],
|
|
172
|
+
"description": "Physical principle; matches equipment brewer.mechanism."
|
|
173
|
+
},
|
|
174
|
+
"description": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"minLength": 20,
|
|
177
|
+
"maxLength": 600,
|
|
178
|
+
"description": "Short neutral explanation in our own words."
|
|
179
|
+
},
|
|
180
|
+
"introduced_year": {
|
|
181
|
+
"type": "integer",
|
|
182
|
+
"minimum": 1000,
|
|
183
|
+
"maximum": 2100,
|
|
184
|
+
"description": "Year the method or device was invented or first sold."
|
|
185
|
+
},
|
|
186
|
+
"origin_country_code": {
|
|
187
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/country_code"
|
|
188
|
+
},
|
|
189
|
+
"equipment_types": {
|
|
190
|
+
"type": "array",
|
|
191
|
+
"uniqueItems": true,
|
|
192
|
+
"description": "Equipment types needed, from the equipment schema's type list.",
|
|
193
|
+
"items": {
|
|
194
|
+
"$ref": "https://schema.roastbook.coffee/equipment/equipment.schema.v0.4.0.json#/$defs/type"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"typical_parameters": {
|
|
198
|
+
"title": "Typical parameters",
|
|
199
|
+
"type": "object",
|
|
200
|
+
"additionalProperties": false,
|
|
201
|
+
"description": "Common starting ranges, not rules.",
|
|
202
|
+
"properties": {
|
|
203
|
+
"dose_g": {
|
|
204
|
+
"$ref": "#/$defs/range"
|
|
205
|
+
},
|
|
206
|
+
"brew_ratio": {
|
|
207
|
+
"$ref": "#/$defs/range",
|
|
208
|
+
"description": "Grams of brew water per gram of coffee, for example 16 for 1:16. Used for filter, immersion and cold brew. A journal that keeps one ratio and a basis puts the ratio here when the basis is brew water (Roastbook: ratioBasis brew_water)."
|
|
209
|
+
},
|
|
210
|
+
"yield_ratio": {
|
|
211
|
+
"$ref": "#/$defs/range",
|
|
212
|
+
"description": "Grams of drink in the cup per gram of coffee, for example 2 for a 1:2 espresso. Used where the drink is weighed, mainly espresso. A journal that keeps one ratio and a basis puts the ratio here when the basis is the yield (Roastbook: ratioBasis target_yield)."
|
|
213
|
+
},
|
|
214
|
+
"brew_temperature_celsius": {
|
|
215
|
+
"$ref": "#/$defs/range",
|
|
216
|
+
"description": "Temperature of the brew water, in degrees Celsius."
|
|
217
|
+
},
|
|
218
|
+
"brew_time_seconds": {
|
|
219
|
+
"$ref": "#/$defs/range"
|
|
220
|
+
},
|
|
221
|
+
"brew_pressure_bar": {
|
|
222
|
+
"$ref": "#/$defs/range"
|
|
223
|
+
},
|
|
224
|
+
"grind_size": {
|
|
225
|
+
"type": "array",
|
|
226
|
+
"uniqueItems": true,
|
|
227
|
+
"minItems": 1,
|
|
228
|
+
"items": {
|
|
229
|
+
"$ref": "#/$defs/grind_size"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"grind_d50_microns": {
|
|
233
|
+
"$ref": "#/$defs/range",
|
|
234
|
+
"description": "Median particle size in micrometres, comparable across grinders."
|
|
235
|
+
},
|
|
236
|
+
"paper_filter_position": {
|
|
237
|
+
"type": "array",
|
|
238
|
+
"uniqueItems": true,
|
|
239
|
+
"minItems": 1,
|
|
240
|
+
"items": {
|
|
241
|
+
"$ref": "#/$defs/paper_filter_position"
|
|
242
|
+
},
|
|
243
|
+
"description": "Paper filter positions commonly used with this method."
|
|
244
|
+
},
|
|
245
|
+
"distribution_method": {
|
|
246
|
+
"type": "array",
|
|
247
|
+
"uniqueItems": true,
|
|
248
|
+
"minItems": 1,
|
|
249
|
+
"items": {
|
|
250
|
+
"$ref": "#/$defs/distribution_method"
|
|
251
|
+
},
|
|
252
|
+
"description": "Distribution methods commonly used with this method."
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"recorded_parameters": {
|
|
257
|
+
"type": "array",
|
|
258
|
+
"uniqueItems": true,
|
|
259
|
+
"items": {
|
|
260
|
+
"$ref": "#/$defs/parameter"
|
|
261
|
+
},
|
|
262
|
+
"description": "Parameters a coffee journal should offer when logging a brew with this method."
|
|
263
|
+
},
|
|
264
|
+
"typical_drinks": {
|
|
265
|
+
"type": "array",
|
|
266
|
+
"uniqueItems": true,
|
|
267
|
+
"items": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"minLength": 1
|
|
270
|
+
},
|
|
271
|
+
"description": "Drinks usually made with this method, in plain lowercase English, for example espresso, cappuccino or flat white."
|
|
272
|
+
},
|
|
273
|
+
"xref": {
|
|
274
|
+
"type": "object",
|
|
275
|
+
"additionalProperties": false,
|
|
276
|
+
"properties": {
|
|
277
|
+
"wikidata": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"pattern": "^Q[1-9][0-9]*$",
|
|
280
|
+
"description": "Wikidata item id of the method, for example Q180289 for espresso."
|
|
281
|
+
},
|
|
282
|
+
"beanconqueror": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"pattern": "^[A-Z0-9_]+$",
|
|
285
|
+
"description": "Beanconqueror PREPARATION_TYPES key, for app imports."
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
"description": "Identifiers of the method in other datasets and apps."
|
|
289
|
+
},
|
|
290
|
+
"image": {
|
|
291
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/image"
|
|
292
|
+
},
|
|
293
|
+
"checked_at": {
|
|
294
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.4.0.json#/$defs/datetime"
|
|
295
|
+
},
|
|
296
|
+
"filter_types": {
|
|
297
|
+
"type": "array",
|
|
298
|
+
"uniqueItems": true,
|
|
299
|
+
"items": {
|
|
300
|
+
"enum": [
|
|
301
|
+
"paper",
|
|
302
|
+
"metal",
|
|
303
|
+
"cloth",
|
|
304
|
+
"none"
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
"description": "Filters the method can use. paper: paper filters; metal: metal mesh or perforated metal; cloth: cloth filters; none: no filter (for example cezve or cold brew in a jar)."
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schema.roastbook.coffee/brew-methods/brew-methods.schema.v0.1.0.json",
|
|
4
|
+
"title": "Open Coffee brew methods",
|
|
5
|
+
"description": "Ways of brewing coffee (espresso, pour-over, V60, AeroPress, cold brew…), their typical parameters and what a coffee journal should record for each. Methods form a tree: a device-specific method such as v60 has the general method pour-over as parent. Part of Roastbook Open Coffee.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"$schema",
|
|
9
|
+
"meta",
|
|
10
|
+
"brew_methods"
|
|
11
|
+
],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"$schema": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^https://schema\\.roastbook\\.coffee/brew-methods/brew-methods\\.schema\\.v[0-9]+\\.[0-9]+\\.[0-9]+\\.json$",
|
|
17
|
+
"description": "Canonical URL of the schema version this file follows."
|
|
18
|
+
},
|
|
19
|
+
"meta": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/dataset_meta",
|
|
22
|
+
"unevaluatedProperties": false
|
|
23
|
+
},
|
|
24
|
+
"brew_methods": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"$ref": "#/$defs/brew_method"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"$defs": {
|
|
32
|
+
"range": {
|
|
33
|
+
"title": "Range",
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"minProperties": 1,
|
|
37
|
+
"properties": {
|
|
38
|
+
"min": {
|
|
39
|
+
"type": "number",
|
|
40
|
+
"minimum": 0,
|
|
41
|
+
"description": "Lowest common value, in the unit named by the parameter (for example grams for dose_g, degrees Celsius for water_temperature_celsius)."
|
|
42
|
+
},
|
|
43
|
+
"max": {
|
|
44
|
+
"type": "number",
|
|
45
|
+
"minimum": 0,
|
|
46
|
+
"description": "Highest common value, in the unit named by the parameter."
|
|
47
|
+
},
|
|
48
|
+
"typical": {
|
|
49
|
+
"type": "number",
|
|
50
|
+
"minimum": 0,
|
|
51
|
+
"description": "A good starting value, in the unit named by the parameter."
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"parameter": {
|
|
56
|
+
"title": "Brew parameter",
|
|
57
|
+
"enum": [
|
|
58
|
+
"dose_g",
|
|
59
|
+
"brew_water_g",
|
|
60
|
+
"yield_g",
|
|
61
|
+
"ratio",
|
|
62
|
+
"grind_setting",
|
|
63
|
+
"brew_time_seconds",
|
|
64
|
+
"target_time_seconds",
|
|
65
|
+
"water_temperature_celsius",
|
|
66
|
+
"bloom_time_seconds",
|
|
67
|
+
"bloom_water_g",
|
|
68
|
+
"preinfusion_time_seconds",
|
|
69
|
+
"preinfusion_pressure_bar",
|
|
70
|
+
"brew_pressure_bar",
|
|
71
|
+
"flow_rate_ml_per_second",
|
|
72
|
+
"tamp_force_kg",
|
|
73
|
+
"paper_filter_position",
|
|
74
|
+
"distribution_method",
|
|
75
|
+
"pour_count",
|
|
76
|
+
"steep_time_seconds"
|
|
77
|
+
],
|
|
78
|
+
"description": "Parameter keys shared with coffee journals such as Roastbook. ratio is brew water to coffee (16 means 1:16); for espresso it is yield to dose (2 means 1:2)."
|
|
79
|
+
},
|
|
80
|
+
"grind_size": {
|
|
81
|
+
"title": "Grind size",
|
|
82
|
+
"enum": [
|
|
83
|
+
"extra_fine",
|
|
84
|
+
"fine",
|
|
85
|
+
"medium_fine",
|
|
86
|
+
"medium",
|
|
87
|
+
"medium_coarse",
|
|
88
|
+
"coarse",
|
|
89
|
+
"extra_coarse"
|
|
90
|
+
],
|
|
91
|
+
"description": "Descriptive grind size; extra_fine is Turkish, fine espresso, medium filter, coarse French press and cold brew."
|
|
92
|
+
},
|
|
93
|
+
"brew_method": {
|
|
94
|
+
"title": "Brew method",
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"required": [
|
|
98
|
+
"id",
|
|
99
|
+
"name",
|
|
100
|
+
"category",
|
|
101
|
+
"extraction",
|
|
102
|
+
"description",
|
|
103
|
+
"checked_at"
|
|
104
|
+
],
|
|
105
|
+
"properties": {
|
|
106
|
+
"id": {
|
|
107
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/id",
|
|
108
|
+
"description": "Stable readable slug, e.g. v60, french-press. Never reused.",
|
|
109
|
+
"examples": [
|
|
110
|
+
"v60"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"name": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"minLength": 1,
|
|
116
|
+
"description": "Common English name of the method, for example Pour-over or French press."
|
|
117
|
+
},
|
|
118
|
+
"aliases": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"uniqueItems": true,
|
|
121
|
+
"items": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"minLength": 1
|
|
124
|
+
},
|
|
125
|
+
"description": "Other names in any language, e.g. Filterkaffee, drip."
|
|
126
|
+
},
|
|
127
|
+
"parent": {
|
|
128
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/id",
|
|
129
|
+
"description": "The more general method, e.g. pour-over for v60."
|
|
130
|
+
},
|
|
131
|
+
"category": {
|
|
132
|
+
"enum": [
|
|
133
|
+
"espresso",
|
|
134
|
+
"filter",
|
|
135
|
+
"immersion",
|
|
136
|
+
"pressure",
|
|
137
|
+
"vacuum",
|
|
138
|
+
"boiling",
|
|
139
|
+
"cold"
|
|
140
|
+
],
|
|
141
|
+
"description": "Family used for grouping: espresso (9-bar style pressure), filter (percolation through a filter), immersion (steep, then separate), pressure (non-espresso pressure such as AeroPress or moka pot), vacuum (siphon), boiling (cezve/ibrik), cold (cold water over hours)."
|
|
142
|
+
},
|
|
143
|
+
"extraction": {
|
|
144
|
+
"enum": [
|
|
145
|
+
"percolation",
|
|
146
|
+
"immersion",
|
|
147
|
+
"hybrid"
|
|
148
|
+
],
|
|
149
|
+
"description": "Physical principle; matches equipment brewer.mechanism."
|
|
150
|
+
},
|
|
151
|
+
"description": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"minLength": 20,
|
|
154
|
+
"maxLength": 600,
|
|
155
|
+
"description": "Short neutral explanation in our own words."
|
|
156
|
+
},
|
|
157
|
+
"introduced_year": {
|
|
158
|
+
"type": "integer",
|
|
159
|
+
"minimum": 1000,
|
|
160
|
+
"maximum": 2100,
|
|
161
|
+
"description": "Year the method or device was invented or first sold."
|
|
162
|
+
},
|
|
163
|
+
"origin_country_code": {
|
|
164
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/country_code"
|
|
165
|
+
},
|
|
166
|
+
"equipment_types": {
|
|
167
|
+
"type": "array",
|
|
168
|
+
"uniqueItems": true,
|
|
169
|
+
"description": "Equipment types needed, from the equipment schema's type list.",
|
|
170
|
+
"items": {
|
|
171
|
+
"$ref": "https://schema.roastbook.coffee/equipment/equipment.schema.v0.1.0.json#/$defs/type"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"typical_parameters": {
|
|
175
|
+
"title": "Typical parameters",
|
|
176
|
+
"type": "object",
|
|
177
|
+
"additionalProperties": false,
|
|
178
|
+
"description": "Common starting ranges, not rules.",
|
|
179
|
+
"properties": {
|
|
180
|
+
"dose_g": {
|
|
181
|
+
"$ref": "#/$defs/range"
|
|
182
|
+
},
|
|
183
|
+
"ratio": {
|
|
184
|
+
"$ref": "#/$defs/range"
|
|
185
|
+
},
|
|
186
|
+
"water_temperature_celsius": {
|
|
187
|
+
"$ref": "#/$defs/range"
|
|
188
|
+
},
|
|
189
|
+
"brew_time_seconds": {
|
|
190
|
+
"$ref": "#/$defs/range"
|
|
191
|
+
},
|
|
192
|
+
"brew_pressure_bar": {
|
|
193
|
+
"$ref": "#/$defs/range"
|
|
194
|
+
},
|
|
195
|
+
"grind_size": {
|
|
196
|
+
"type": "array",
|
|
197
|
+
"uniqueItems": true,
|
|
198
|
+
"minItems": 1,
|
|
199
|
+
"items": {
|
|
200
|
+
"$ref": "#/$defs/grind_size"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"grind_d50_microns": {
|
|
204
|
+
"$ref": "#/$defs/range",
|
|
205
|
+
"description": "Median particle size in micrometres, comparable across grinders."
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"recorded_parameters": {
|
|
210
|
+
"type": "array",
|
|
211
|
+
"uniqueItems": true,
|
|
212
|
+
"items": {
|
|
213
|
+
"$ref": "#/$defs/parameter"
|
|
214
|
+
},
|
|
215
|
+
"description": "Parameters a coffee journal should offer when logging a brew with this method."
|
|
216
|
+
},
|
|
217
|
+
"typical_drinks": {
|
|
218
|
+
"type": "array",
|
|
219
|
+
"uniqueItems": true,
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"minLength": 1
|
|
223
|
+
},
|
|
224
|
+
"description": "Drinks usually made with this method, in plain lowercase English, for example espresso, cappuccino or flat white."
|
|
225
|
+
},
|
|
226
|
+
"xref": {
|
|
227
|
+
"type": "object",
|
|
228
|
+
"additionalProperties": false,
|
|
229
|
+
"properties": {
|
|
230
|
+
"wikidata": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"pattern": "^Q[1-9][0-9]*$",
|
|
233
|
+
"description": "Wikidata item id of the method, for example Q180289 for espresso."
|
|
234
|
+
},
|
|
235
|
+
"beanconqueror": {
|
|
236
|
+
"type": "string",
|
|
237
|
+
"pattern": "^[A-Z0-9_]+$",
|
|
238
|
+
"description": "Beanconqueror PREPARATION_TYPES key, for app imports."
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"description": "Identifiers of the method in other datasets and apps."
|
|
242
|
+
},
|
|
243
|
+
"image": {
|
|
244
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/image"
|
|
245
|
+
},
|
|
246
|
+
"checked_at": {
|
|
247
|
+
"$ref": "https://schema.roastbook.coffee/common/common.schema.v0.1.0.json#/$defs/datetime"
|
|
248
|
+
},
|
|
249
|
+
"filter_types": {
|
|
250
|
+
"type": "array",
|
|
251
|
+
"uniqueItems": true,
|
|
252
|
+
"items": {
|
|
253
|
+
"enum": [
|
|
254
|
+
"paper",
|
|
255
|
+
"metal",
|
|
256
|
+
"cloth",
|
|
257
|
+
"none"
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
"description": "Filters the method can use. paper: paper filters; metal: metal mesh or perforated metal; cloth: cloth filters; none: no filter (for example cezve or cold brew in a jar)."
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|