contentbase 0.0.1

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 (89) hide show
  1. package/README.md +460 -0
  2. package/bun.lock +473 -0
  3. package/examples/sdlc-queries.ts +161 -0
  4. package/package.json +41 -0
  5. package/showcases/national-parks/models.ts +74 -0
  6. package/showcases/national-parks/parks/acadia.mdx +40 -0
  7. package/showcases/national-parks/parks/yosemite.mdx +44 -0
  8. package/showcases/national-parks/parks/zion.mdx +44 -0
  9. package/showcases/national-parks/queries.ts +103 -0
  10. package/showcases/national-parks/trails/angels-landing.mdx +19 -0
  11. package/showcases/national-parks/trails/cathedral-lakes.mdx +19 -0
  12. package/showcases/national-parks/trails/half-dome.mdx +19 -0
  13. package/showcases/national-parks/trails/jordan-pond-path.mdx +19 -0
  14. package/showcases/national-parks/trails/mist-trail.mdx +19 -0
  15. package/showcases/national-parks/trails/observation-point.mdx +19 -0
  16. package/showcases/national-parks/trails/precipice-trail.mdx +19 -0
  17. package/showcases/national-parks/trails/the-narrows.mdx +19 -0
  18. package/showcases/recipes/cuisines/chinese.mdx +28 -0
  19. package/showcases/recipes/cuisines/italian.mdx +32 -0
  20. package/showcases/recipes/cuisines/mexican.mdx +28 -0
  21. package/showcases/recipes/models.ts +77 -0
  22. package/showcases/recipes/queries.ts +89 -0
  23. package/showcases/recipes/recipes/chinese/egg-fried-rice.mdx +43 -0
  24. package/showcases/recipes/recipes/chinese/mapo-tofu.mdx +47 -0
  25. package/showcases/recipes/recipes/italian/bruschetta.mdx +38 -0
  26. package/showcases/recipes/recipes/italian/cacio-e-pepe.mdx +39 -0
  27. package/showcases/recipes/recipes/italian/tiramisu.mdx +43 -0
  28. package/showcases/recipes/recipes/mexican/chicken-tinga.mdx +44 -0
  29. package/showcases/recipes/recipes/mexican/guacamole.mdx +39 -0
  30. package/showcases/vinyl-collection/albums/bitches-brew.mdx +36 -0
  31. package/showcases/vinyl-collection/albums/i-put-a-spell-on-you.mdx +35 -0
  32. package/showcases/vinyl-collection/albums/in-rainbows.mdx +35 -0
  33. package/showcases/vinyl-collection/albums/kind-of-blue.mdx +32 -0
  34. package/showcases/vinyl-collection/albums/ok-computer.mdx +37 -0
  35. package/showcases/vinyl-collection/albums/wild-is-the-wind.mdx +35 -0
  36. package/showcases/vinyl-collection/artists/miles-davis.mdx +27 -0
  37. package/showcases/vinyl-collection/artists/nina-simone.mdx +26 -0
  38. package/showcases/vinyl-collection/artists/radiohead.mdx +27 -0
  39. package/showcases/vinyl-collection/models.ts +73 -0
  40. package/showcases/vinyl-collection/queries.ts +87 -0
  41. package/src/ast-query.ts +132 -0
  42. package/src/cli/commands/action.ts +44 -0
  43. package/src/cli/commands/create.ts +59 -0
  44. package/src/cli/commands/export.ts +24 -0
  45. package/src/cli/commands/init.ts +75 -0
  46. package/src/cli/commands/inspect.ts +46 -0
  47. package/src/cli/commands/validate.ts +75 -0
  48. package/src/cli/index.ts +20 -0
  49. package/src/cli/load-collection.ts +53 -0
  50. package/src/collection.ts +399 -0
  51. package/src/define-model.ts +80 -0
  52. package/src/document.ts +468 -0
  53. package/src/index.ts +47 -0
  54. package/src/model-instance.ts +227 -0
  55. package/src/node-shortcuts.ts +87 -0
  56. package/src/parse.ts +123 -0
  57. package/src/query/collection-query.ts +149 -0
  58. package/src/query/index.ts +5 -0
  59. package/src/query/operators.ts +37 -0
  60. package/src/query/query-builder.ts +109 -0
  61. package/src/relationships/belongs-to.ts +50 -0
  62. package/src/relationships/has-many.ts +136 -0
  63. package/src/relationships/index.ts +57 -0
  64. package/src/relationships/types.ts +7 -0
  65. package/src/section.ts +29 -0
  66. package/src/types.ts +221 -0
  67. package/src/utils/index.ts +11 -0
  68. package/src/utils/inflect.ts +82 -0
  69. package/src/utils/normalize-headings.ts +31 -0
  70. package/src/utils/parse-table.ts +30 -0
  71. package/src/utils/read-directory.ts +35 -0
  72. package/src/utils/stringify-ast.ts +9 -0
  73. package/src/validator.ts +52 -0
  74. package/test/ast-query.test.ts +128 -0
  75. package/test/collection.test.ts +99 -0
  76. package/test/define-model.test.ts +78 -0
  77. package/test/document.test.ts +225 -0
  78. package/test/fixtures/sdlc/epics/authentication.mdx +42 -0
  79. package/test/fixtures/sdlc/epics/searching-and-browsing.mdx +21 -0
  80. package/test/fixtures/sdlc/models.ts +89 -0
  81. package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-register.mdx +20 -0
  82. package/test/helpers.ts +21 -0
  83. package/test/model-instance.test.ts +197 -0
  84. package/test/query.test.ts +167 -0
  85. package/test/relationships.test.ts +84 -0
  86. package/test/section.test.ts +99 -0
  87. package/test/validator.test.ts +62 -0
  88. package/tsconfig.json +18 -0
  89. package/vitest.config.ts +11 -0
@@ -0,0 +1,28 @@
1
+ ---
2
+ region: East Asia
3
+ spiceLevel: medium
4
+ ---
5
+
6
+ # Chinese
7
+
8
+ Chinese cuisine spans thousands of years and dozens of regional traditions. The emphasis on wok technique, balancing the five flavors, and textural contrast sets it apart.
9
+
10
+ ## Staple Ingredients
11
+
12
+ - Soy sauce
13
+ - Sesame oil
14
+ - Rice vinegar
15
+ - Ginger
16
+ - Scallions
17
+ - Shaoxing wine
18
+ - Cornstarch
19
+
20
+ ## Recipes
21
+
22
+ ### Mapo Tofu
23
+
24
+ Silken tofu in a fiery, numbing sauce — the signature dish of Sichuan province.
25
+
26
+ ### Egg Fried Rice
27
+
28
+ The ultimate pantry-clearing weeknight meal, elevated by proper wok technique.
@@ -0,0 +1,32 @@
1
+ ---
2
+ region: Southern Europe
3
+ spiceLevel: mild
4
+ ---
5
+
6
+ # Italian
7
+
8
+ Italian cooking centers on simplicity — fresh, high-quality ingredients prepared with minimal fuss. Regional variation is enormous, from the butter-rich dishes of the north to the olive-oil-and-tomato base of the south.
9
+
10
+ ## Staple Ingredients
11
+
12
+ - Olive oil
13
+ - Garlic
14
+ - San Marzano tomatoes
15
+ - Parmigiano-Reggiano
16
+ - Fresh basil
17
+ - Dried pasta
18
+ - Mozzarella
19
+
20
+ ## Recipes
21
+
22
+ ### Cacio e Pepe
23
+
24
+ A Roman classic that turns three ingredients into something transcendent.
25
+
26
+ ### Tiramisu
27
+
28
+ The iconic layered coffee dessert — no baking required.
29
+
30
+ ### Bruschetta
31
+
32
+ Toasted bread with ripe tomatoes, garlic, and basil.
@@ -0,0 +1,28 @@
1
+ ---
2
+ region: North America
3
+ spiceLevel: hot
4
+ ---
5
+
6
+ # Mexican
7
+
8
+ Mexican food is built on a foundation of corn, beans, and chiles that predates European contact. The layering of salsas, fresh garnishes, and slow-cooked meats makes it endlessly satisfying.
9
+
10
+ ## Staple Ingredients
11
+
12
+ - Dried chiles (ancho, guajillo, chipotle)
13
+ - Corn tortillas
14
+ - Black beans
15
+ - Cilantro
16
+ - Limes
17
+ - Cumin
18
+ - Avocado
19
+
20
+ ## Recipes
21
+
22
+ ### Guacamole
23
+
24
+ The essential dip — ripe avocados, lime, and a little heat.
25
+
26
+ ### Chicken Tinga
27
+
28
+ Shredded chicken braised in a smoky chipotle-tomato sauce.
@@ -0,0 +1,77 @@
1
+ import {
2
+ defineModel,
3
+ section,
4
+ hasMany,
5
+ belongsTo,
6
+ z,
7
+ } from "../../src/index";
8
+ import { toString } from "mdast-util-to-string";
9
+ import { parseTable } from "../../src/utils/parse-table";
10
+
11
+ // ─── Cuisine (parent) ───
12
+
13
+ export const Cuisine = defineModel("Cuisine", {
14
+ prefix: "cuisines",
15
+ meta: z.object({
16
+ region: z.string(),
17
+ spiceLevel: z.enum(["mild", "medium", "hot", "very-hot"]).optional(),
18
+ }),
19
+ sections: {
20
+ stapleIngredients: section("Staple Ingredients", {
21
+ extract: (q) => q.selectAll("listItem").map((n) => toString(n)),
22
+ schema: z.array(z.string()),
23
+ }),
24
+ },
25
+ relationships: {
26
+ recipes: hasMany(() => Recipe, { heading: "Recipes" }),
27
+ },
28
+ });
29
+
30
+ // ─── Recipe ───
31
+
32
+ export const Recipe = defineModel("Recipe", {
33
+ prefix: "recipes",
34
+ meta: z.object({
35
+ course: z.enum(["appetizer", "main", "side", "dessert", "drink"]),
36
+ servings: z.number(),
37
+ prepTime: z.string(),
38
+ cookTime: z.string(),
39
+ difficulty: z.enum(["easy", "medium", "hard"]).default("medium"),
40
+ cuisine: z.string().optional(),
41
+ vegetarian: z.boolean().default(false),
42
+ }),
43
+ sections: {
44
+ ingredients: section("Ingredients", {
45
+ extract: (q) => {
46
+ const tables = q.selectAll("table");
47
+ if (tables.length > 0) {
48
+ return parseTable(tables[0]);
49
+ }
50
+ return q.selectAll("listItem").map((n) => toString(n));
51
+ },
52
+ schema: z.union([
53
+ z.array(z.record(z.string(), z.string())),
54
+ z.array(z.string()),
55
+ ]),
56
+ }),
57
+ steps: section("Steps", {
58
+ extract: (q) => q.selectAll("listItem").map((n) => toString(n)),
59
+ schema: z.array(z.string()).min(1),
60
+ }),
61
+ notes: section("Notes", {
62
+ extract: (q) => q.selectAll("listItem").map((n) => toString(n)),
63
+ schema: z.array(z.string()),
64
+ }),
65
+ },
66
+ relationships: {
67
+ cuisine: belongsTo(() => Cuisine, {
68
+ foreignKey: (doc) => doc.meta.cuisine as string,
69
+ }),
70
+ },
71
+ computed: {
72
+ isQuick: (self: any) => {
73
+ const mins = parseInt(self.meta.prepTime) + parseInt(self.meta.cookTime);
74
+ return mins <= 30;
75
+ },
76
+ },
77
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Example queries for the Recipes showcase.
3
+ *
4
+ * Run with: bun showcases/recipes/queries.ts
5
+ */
6
+ import { Collection } from "../../src/index";
7
+ import { Cuisine, Recipe } from "./models";
8
+
9
+ const collection = new Collection({
10
+ rootPath: new URL(".", import.meta.url).pathname,
11
+ });
12
+
13
+ collection.register(Cuisine);
14
+ collection.register(Recipe);
15
+ await collection.load();
16
+
17
+ // ── All recipes ──
18
+ const allRecipes = await collection.query(Recipe).fetchAll();
19
+ console.log(`Total recipes: ${allRecipes.length}`);
20
+
21
+ // ── Filter by course ──
22
+ const appetizers = await collection
23
+ .query(Recipe)
24
+ .where("meta.course", "appetizer")
25
+ .fetchAll();
26
+ console.log(`\nAppetizers: ${appetizers.map((r) => r.title).join(", ")}`);
27
+
28
+ const mains = await collection
29
+ .query(Recipe)
30
+ .where("meta.course", "main")
31
+ .fetchAll();
32
+ console.log(`Mains: ${mains.map((r) => r.title).join(", ")}`);
33
+
34
+ const desserts = await collection
35
+ .query(Recipe)
36
+ .where("meta.course", "dessert")
37
+ .fetchAll();
38
+ console.log(`Desserts: ${desserts.map((r) => r.title).join(", ")}`);
39
+
40
+ // ── Vegetarian recipes ──
41
+ const veggie = await collection
42
+ .query(Recipe)
43
+ .where("meta.vegetarian", true)
44
+ .fetchAll();
45
+ console.log(
46
+ `\nVegetarian recipes: ${veggie.map((r) => r.title).join(", ")}`
47
+ );
48
+
49
+ // ── Easy recipes ──
50
+ const easyOnes = await collection
51
+ .query(Recipe)
52
+ .where("meta.difficulty", "easy")
53
+ .fetchAll();
54
+ console.log(`Easy recipes: ${easyOnes.map((r) => r.title).join(", ")}`);
55
+
56
+ // ── Ingredients as structured data ──
57
+ const cacioEPepe = await collection.query(Recipe).first();
58
+ if (cacioEPepe) {
59
+ console.log(`\n--- ${cacioEPepe.title} ---`);
60
+ console.log("Ingredients (table data):", cacioEPepe.sections.ingredients);
61
+ console.log("Steps:", cacioEPepe.sections.steps);
62
+ }
63
+
64
+ // ── Cuisine → Recipes relationship (hasMany) ──
65
+ const italian = collection.getModel("cuisines/italian", Cuisine);
66
+ console.log(`\n--- ${italian.title} Cuisine ---`);
67
+ console.log("Staple ingredients:", italian.sections.stapleIngredients);
68
+ const italianRecipes = italian.relationships.recipes.fetchAll();
69
+ console.log(
70
+ `Recipes under this cuisine: ${italianRecipes.map((r) => r.title).join(", ")}`
71
+ );
72
+
73
+ // ── Recipe → Cuisine relationship (belongsTo) ──
74
+ const mapo = collection.getModel("recipes/chinese/mapo-tofu", Recipe);
75
+ const parentCuisine = mapo.relationships.cuisine.fetch();
76
+ console.log(`\n${mapo.title} belongs to cuisine: ${parentCuisine.title}`);
77
+
78
+ // ── Computed properties ──
79
+ console.log(`\nQuick recipes (<=30 min total):`);
80
+ for (const recipe of allRecipes) {
81
+ console.log(` ${recipe.title}: ${recipe.computed.isQuick ? "yes" : "no"}`);
82
+ }
83
+
84
+ // ── Serialize to JSON ──
85
+ const json = mapo.toJSON({
86
+ sections: ["ingredients", "steps"],
87
+ computed: ["isQuick"],
88
+ });
89
+ console.log(`\nMapo Tofu as JSON:`, JSON.stringify(json, null, 2));
@@ -0,0 +1,43 @@
1
+ ---
2
+ course: main
3
+ servings: 2
4
+ prepTime: "5 min"
5
+ cookTime: "10 min"
6
+ difficulty: easy
7
+ cuisine: chinese
8
+ vegetarian: true
9
+ ---
10
+
11
+ # Egg Fried Rice
12
+
13
+ The secret to great fried rice is day-old rice and a screaming hot wok. This is pantry cooking at its finest — minimal ingredients, maximum technique.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Cooked jasmine rice | 400g | Day-old, cold from fridge |
20
+ | Eggs | 3 | Beaten |
21
+ | Scallions | 3 stalks | Thinly sliced |
22
+ | Soy sauce | 1.5 tbsp | Light soy |
23
+ | Sesame oil | 1 tsp | Toasted |
24
+ | White pepper | Pinch | |
25
+ | Vegetable oil | 2 tbsp | High smoke point |
26
+ | Salt | To taste | |
27
+
28
+ ## Steps
29
+
30
+ 1. Break up the cold rice with your hands so there are no clumps
31
+ 2. Heat a wok over the highest heat until it just starts to smoke
32
+ 3. Add oil, then immediately pour in the beaten eggs
33
+ 4. Scramble the eggs for 10 seconds until just set, then break into pieces
34
+ 5. Add the rice and press it flat against the wok — let it sear without stirring for 30 seconds
35
+ 6. Toss and repeat the searing step twice more until the rice is hot and slightly crispy
36
+ 7. Add soy sauce around the edges of the wok so it sizzles and seasons evenly
37
+ 8. Finish with sesame oil, white pepper, and scallions — one final toss and serve
38
+
39
+ ## Notes
40
+
41
+ - Freshly cooked rice is too wet and will steam instead of fry
42
+ - A carbon steel wok over a gas burner is ideal but cast iron works too
43
+ - The less you stir, the better the char
@@ -0,0 +1,47 @@
1
+ ---
2
+ course: main
3
+ servings: 4
4
+ prepTime: "10 min"
5
+ cookTime: "20 min"
6
+ difficulty: medium
7
+ cuisine: chinese
8
+ vegetarian: false
9
+ ---
10
+
11
+ # Mapo Tofu
12
+
13
+ The definitive Sichuan comfort food. Silky tofu swimming in a fiery, numbing sauce of doubanjiang, chili oil, and fermented black beans. The combination of ma (numbing) and la (spicy) is addictive.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Silken tofu | 400g | Medium-firm, cubed |
20
+ | Ground pork | 150g | Or beef |
21
+ | Doubanjiang (chili bean paste) | 2 tbsp | Pixian brand preferred |
22
+ | Sichuan peppercorns | 1 tbsp | Toasted and ground |
23
+ | Garlic | 4 cloves | Minced |
24
+ | Ginger | 1 tbsp | Minced |
25
+ | Scallions | 4 stalks | White and green separated |
26
+ | Fermented black beans | 1 tbsp | Rinsed and chopped |
27
+ | Soy sauce | 1 tbsp | Light |
28
+ | Cornstarch slurry | 2 tbsp | Mixed with 2 tbsp water |
29
+ | Vegetable oil | 2 tbsp | |
30
+ | Chili flakes | 1 tsp | Optional |
31
+
32
+ ## Steps
33
+
34
+ 1. Gently simmer the cubed tofu in salted water for 5 minutes to warm through and firm it slightly — drain and set aside
35
+ 2. Heat oil in a wok over high heat, then brown the ground pork until the fat renders
36
+ 3. Push the pork to the side and add doubanjiang, stir-frying until the oil turns red
37
+ 4. Add garlic, ginger, scallion whites, and black beans — cook until fragrant
38
+ 5. Pour in a cup of water or stock and bring to a simmer
39
+ 6. Slide in the tofu gently and let it braise for 5 minutes
40
+ 7. Add soy sauce and the cornstarch slurry, tilting the wok to distribute
41
+ 8. Finish with ground Sichuan peppercorn and a shower of scallion greens
42
+
43
+ ## Notes
44
+
45
+ - Blanching the tofu prevents it from breaking apart in the wok
46
+ - Doubanjiang is the soul of this dish — don't substitute with generic chili paste
47
+ - Serve over steamed rice to catch the sauce
@@ -0,0 +1,38 @@
1
+ ---
2
+ course: appetizer
3
+ servings: 6
4
+ prepTime: "10 min"
5
+ cookTime: "5 min"
6
+ difficulty: easy
7
+ cuisine: italian
8
+ vegetarian: true
9
+ ---
10
+
11
+ # Bruschetta
12
+
13
+ The simplest Italian appetizer — and one of the best. Peak-summer tomatoes are essential; don't bother making this with mealy off-season ones.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Ripe tomatoes | 500g | Diced, seeds removed |
20
+ | Garlic | 3 cloves | 1 for rubbing, 2 minced |
21
+ | Fresh basil | 10 leaves | Torn, not chopped |
22
+ | Extra virgin olive oil | 3 tbsp | Good quality |
23
+ | Balsamic vinegar | 1 tsp | Optional |
24
+ | Crusty bread | 1 loaf | Sliced 1cm thick |
25
+ | Salt | To taste | Flaky sea salt is nice |
26
+
27
+ ## Steps
28
+
29
+ 1. Dice the tomatoes, remove seeds, and toss with minced garlic, basil, olive oil, and salt
30
+ 2. Let the tomato mixture sit for 15 minutes so the flavors meld
31
+ 3. Grill or toast the bread slices until golden and crisp
32
+ 4. Rub each warm slice with a halved garlic clove
33
+ 5. Spoon the tomato mixture onto the bread and serve immediately
34
+
35
+ ## Notes
36
+
37
+ - Make this only when you have great tomatoes — it lives or dies by that single ingredient
38
+ - The bread should be sturdy enough to hold the topping without going soggy
@@ -0,0 +1,39 @@
1
+ ---
2
+ course: main
3
+ servings: 4
4
+ prepTime: "5 min"
5
+ cookTime: "15 min"
6
+ difficulty: medium
7
+ cuisine: italian
8
+ vegetarian: true
9
+ ---
10
+
11
+ # Cacio e Pepe
12
+
13
+ A Roman classic that turns three humble ingredients — pasta, Pecorino Romano, and black pepper — into something transcendent. The trick is building a creamy emulsion without any cream.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Tonnarelli or spaghetti | 400g | Dried, not fresh |
20
+ | Pecorino Romano | 200g | Finely grated |
21
+ | Black pepper | 2 tbsp | Freshly cracked, coarse |
22
+ | Pasta water | As needed | Starchy — use a small pot |
23
+ | Salt | To taste | Go easy, the cheese is salty |
24
+
25
+ ## Steps
26
+
27
+ 1. Bring a small pot of salted water to a boil — less water means starchier pasta water, which is key
28
+ 2. Toast the black pepper in a dry skillet over medium heat until fragrant, about 1 minute
29
+ 3. Add a ladle of pasta water to the pepper skillet and let it reduce slightly
30
+ 4. Cook the pasta until 1 minute shy of al dente, then transfer directly to the skillet
31
+ 5. Remove from heat and add grated Pecorino in small handfuls, tossing constantly
32
+ 6. Add splashes of pasta water as needed to form a smooth, creamy sauce
33
+ 7. Serve immediately with extra pepper and cheese on top
34
+
35
+ ## Notes
36
+
37
+ - The pasta water starch is what makes the sauce emulsify — do not rinse the pasta
38
+ - Work quickly once you add the cheese; too much heat will cause it to clump
39
+ - Purists insist on Pecorino only — no Parmigiano
@@ -0,0 +1,43 @@
1
+ ---
2
+ course: dessert
3
+ servings: 8
4
+ prepTime: "30 min"
5
+ cookTime: "0 min"
6
+ difficulty: easy
7
+ cuisine: italian
8
+ vegetarian: true
9
+ ---
10
+
11
+ # Tiramisu
12
+
13
+ The iconic Italian layered dessert. Espresso-soaked ladyfingers and a mascarpone cream that sets overnight into something magical. No baking required.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Mascarpone | 500g | Room temperature |
20
+ | Egg yolks | 6 | Large |
21
+ | Sugar | 120g | Granulated |
22
+ | Savoiardi (ladyfingers) | 300g | The dry, crisp kind |
23
+ | Espresso | 400ml | Cooled to room temp |
24
+ | Cocoa powder | For dusting | Dutch process preferred |
25
+ | Dark rum | 2 tbsp | Optional, or use Marsala |
26
+
27
+ ## Steps
28
+
29
+ 1. Brew the espresso and let it cool completely — stir in the rum if using
30
+ 2. Whisk egg yolks and sugar until thick, pale, and ribbon-like
31
+ 3. Fold the mascarpone into the yolk mixture gently until smooth
32
+ 4. Quickly dip each ladyfinger into the espresso — just a brief dunk, not a soak
33
+ 5. Arrange a layer of dipped ladyfingers in the bottom of a dish
34
+ 6. Spread half the mascarpone cream over the ladyfingers
35
+ 7. Repeat with a second layer of ladyfingers and the remaining cream
36
+ 8. Cover and refrigerate for at least 6 hours, preferably overnight
37
+ 9. Dust generously with cocoa powder before serving
38
+
39
+ ## Notes
40
+
41
+ - The ladyfingers should be damp, not soggy — a one-second dip is enough
42
+ - This must rest overnight for the flavors to meld and the texture to set
43
+ - Keeps well in the fridge for 3 days
@@ -0,0 +1,44 @@
1
+ ---
2
+ course: main
3
+ servings: 6
4
+ prepTime: "15 min"
5
+ cookTime: "35 min"
6
+ difficulty: medium
7
+ cuisine: mexican
8
+ vegetarian: false
9
+ ---
10
+
11
+ # Chicken Tinga
12
+
13
+ Shredded chicken braised in a smoky chipotle-tomato sauce. Originally from Puebla, this is the kind of dish that gets better as leftovers. Pile it into tostadas, tacos, or just eat it over rice.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Chicken thighs | 700g | Boneless, skinless |
20
+ | Chipotles in adobo | 3 chiles | Plus 2 tbsp adobo sauce |
21
+ | Crushed tomatoes | 400g | One can |
22
+ | White onion | 1 large | Sliced |
23
+ | Garlic | 4 cloves | Minced |
24
+ | Dried oregano | 1 tsp | Mexican oregano preferred |
25
+ | Cumin | 1/2 tsp | Ground |
26
+ | Chicken broth | 1/2 cup | |
27
+ | Vegetable oil | 1 tbsp | |
28
+ | Salt | To taste | |
29
+
30
+ ## Steps
31
+
32
+ 1. Season the chicken thighs with salt and sear in oil until golden on both sides — remove and set aside
33
+ 2. In the same pot, soften the onion slices until translucent
34
+ 3. Add garlic, oregano, and cumin — cook until fragrant
35
+ 4. Add the crushed tomatoes, chipotles, adobo sauce, and broth — stir and bring to a simmer
36
+ 5. Return the chicken to the pot, cover, and braise on low for 25 minutes
37
+ 6. Remove the chicken and shred with two forks
38
+ 7. Return the shredded chicken to the sauce and simmer uncovered for 5 more minutes to thicken
39
+
40
+ ## Notes
41
+
42
+ - Adjust the number of chipotles to control the heat level
43
+ - This freezes beautifully for up to 3 months
44
+ - Serve on tostadas with crema, pickled onions, and crumbled queso fresco
@@ -0,0 +1,39 @@
1
+ ---
2
+ course: appetizer
3
+ servings: 4
4
+ prepTime: "10 min"
5
+ cookTime: "0 min"
6
+ difficulty: easy
7
+ cuisine: mexican
8
+ vegetarian: true
9
+ ---
10
+
11
+ # Guacamole
12
+
13
+ Ripe avocados, lime, and a little heat. The key is restraint — don't over-mash, don't over-season, and don't make it more than 30 minutes ahead.
14
+
15
+ ## Ingredients
16
+
17
+ | Ingredient | Amount | Notes |
18
+ | --- | --- | --- |
19
+ | Ripe avocados | 3 | Hass, should yield to gentle pressure |
20
+ | Lime juice | 2 tbsp | Fresh |
21
+ | Red onion | 1/4 cup | Finely diced |
22
+ | Serrano chile | 1 | Seeded and minced |
23
+ | Cilantro | 3 tbsp | Chopped |
24
+ | Salt | 3/4 tsp | Kosher |
25
+ | Ripe tomato | 1 small | Diced, optional |
26
+
27
+ ## Steps
28
+
29
+ 1. Halve the avocados, remove the pit, and scoop the flesh into a bowl
30
+ 2. Add salt and lime juice, then mash with a fork to your preferred texture — leave it chunky
31
+ 3. Fold in the onion, serrano, cilantro, and tomato if using
32
+ 4. Taste and adjust salt and lime
33
+ 5. Serve immediately with warm tortilla chips
34
+
35
+ ## Notes
36
+
37
+ - The avocados must be ripe — there is no fixing an unripe avocado
38
+ - A molcajete gives the best texture, but a fork works fine
39
+ - Press plastic wrap directly onto the surface to prevent browning if you must store it
@@ -0,0 +1,36 @@
1
+ ---
2
+ artist: miles-davis
3
+ year: 1970
4
+ genre: Jazz Fusion
5
+ format: 2xLP
6
+ rating: 5
7
+ condition: good
8
+ ---
9
+
10
+ # Bitches Brew
11
+
12
+ Three days in August 1969. Miles put electric keyboards, electric bass, and multiple drummers in a studio and told them to play. The result blew jazz wide open and laid the groundwork for an entire genre.
13
+
14
+ ## Tracklist
15
+
16
+ | # | Title | Duration |
17
+ | --- | --- | --- |
18
+ | 1 | Pharaoh's Dance | 20:05 |
19
+ | 2 | Bitches Brew | 26:58 |
20
+ | 3 | Spanish Key | 17:32 |
21
+ | 4 | John McLaughlin | 4:23 |
22
+ | 5 | Miles Runs the Voodoo Down | 14:02 |
23
+ | 6 | Sanctuary | 10:56 |
24
+
25
+ ## Personnel
26
+
27
+ - Miles Davis — trumpet
28
+ - Wayne Shorter — soprano saxophone
29
+ - Bennie Maupin — bass clarinet
30
+ - Joe Zawinul — electric piano
31
+ - Chick Corea — electric piano
32
+ - John McLaughlin — electric guitar
33
+ - Dave Holland — bass
34
+ - Harvey Brooks — electric bass
35
+ - Lenny White — drums
36
+ - Jack DeJohnette — drums
@@ -0,0 +1,35 @@
1
+ ---
2
+ artist: nina-simone
3
+ year: 1965
4
+ genre: Jazz / Vocal
5
+ format: LP
6
+ rating: 5
7
+ condition: good
8
+ ---
9
+
10
+ # I Put a Spell on You
11
+
12
+ Nina Simone at the height of her powers, backed by lush orchestral arrangements from Hal Mooney. This album captures her ability to inhabit a song so completely that it becomes hers forever, regardless of who wrote it.
13
+
14
+ ## Tracklist
15
+
16
+ | # | Title | Duration |
17
+ | --- | --- | --- |
18
+ | 1 | I Put a Spell on You | 2:34 |
19
+ | 2 | Tomorrow Is My Turn | 2:54 |
20
+ | 3 | Ne Me Quitte Pas | 3:35 |
21
+ | 4 | Marriage Is for Old Folks | 2:13 |
22
+ | 5 | July Tree | 2:17 |
23
+ | 6 | Gimme Some | 2:47 |
24
+ | 7 | Feeling Good | 2:54 |
25
+ | 8 | One September Day | 4:57 |
26
+ | 9 | Blues on Purpose | 2:35 |
27
+ | 10 | Beautiful Land | 2:44 |
28
+ | 11 | You've Got to Learn | 4:42 |
29
+ | 12 | Take Care of Business | 3:11 |
30
+
31
+ ## Personnel
32
+
33
+ - Nina Simone — vocals, piano
34
+ - Hal Mooney — orchestral arrangements
35
+ - Rudy Van Gelder — recording engineer
@@ -0,0 +1,35 @@
1
+ ---
2
+ artist: radiohead
3
+ year: 2007
4
+ genre: Art Rock
5
+ format: LP
6
+ rating: 4
7
+ condition: mint
8
+ ---
9
+
10
+ # In Rainbows
11
+
12
+ After the icy electronics of Kid A and Amnesiac, Radiohead made their warmest, most human record. The songs feel lived-in, generous, and — for once — occasionally happy.
13
+
14
+ ## Tracklist
15
+
16
+ | # | Title | Duration |
17
+ | --- | --- | --- |
18
+ | 1 | 15 Step | 3:57 |
19
+ | 2 | Bodysnatchers | 4:02 |
20
+ | 3 | Nude | 4:15 |
21
+ | 4 | Weird Fishes/Arpeggi | 5:18 |
22
+ | 5 | All I Need | 3:49 |
23
+ | 6 | Faust Arp | 2:10 |
24
+ | 7 | Reckoner | 4:50 |
25
+ | 8 | House of Cards | 5:28 |
26
+ | 9 | Jigsaw Falling Into Place | 4:09 |
27
+ | 10 | Videotape | 4:40 |
28
+
29
+ ## Personnel
30
+
31
+ - Thom Yorke — vocals, guitar, piano
32
+ - Jonny Greenwood — guitar, ondes Martenot, analogue synthesizers
33
+ - Ed O'Brien — guitar, effects
34
+ - Colin Greenwood — bass
35
+ - Phil Selway — drums