contentbase 0.0.4 → 0.0.5
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/.mcp.json +0 -9
- package/MCP-DESCRIPTIONS-REVIEW.md +0 -122
- package/bun.lock +0 -512
- package/dist/cnotes +0 -0
- package/examples/sdlc-queries.ts +0 -161
- package/public/web-demo/index.html +0 -813
- package/scripts/examples/01-collection-setup.ts +0 -46
- package/scripts/examples/02-querying.ts +0 -67
- package/scripts/examples/03-sections.ts +0 -36
- package/scripts/examples/04-relationships.ts +0 -54
- package/scripts/examples/05-document-api.ts +0 -54
- package/scripts/examples/06-extract-sections.ts +0 -55
- package/scripts/examples/07-validation.ts +0 -46
- package/scripts/examples/08-serialization.ts +0 -51
- package/scripts/examples/lib/format.ts +0 -87
- package/scripts/examples/lib/setup.ts +0 -21
- package/scripts/examples/run-all.ts +0 -43
- package/showcases/national-parks/models.ts +0 -74
- package/showcases/national-parks/parks/acadia.mdx +0 -40
- package/showcases/national-parks/parks/yosemite.mdx +0 -44
- package/showcases/national-parks/parks/zion.mdx +0 -44
- package/showcases/national-parks/queries.ts +0 -103
- package/showcases/national-parks/trails/angels-landing.mdx +0 -19
- package/showcases/national-parks/trails/cathedral-lakes.mdx +0 -19
- package/showcases/national-parks/trails/half-dome.mdx +0 -19
- package/showcases/national-parks/trails/jordan-pond-path.mdx +0 -19
- package/showcases/national-parks/trails/mist-trail.mdx +0 -19
- package/showcases/national-parks/trails/observation-point.mdx +0 -19
- package/showcases/national-parks/trails/precipice-trail.mdx +0 -19
- package/showcases/national-parks/trails/the-narrows.mdx +0 -19
- package/showcases/node_modules/.cache/.repl_history +0 -3
- package/showcases/recipes/cuisines/chinese.mdx +0 -28
- package/showcases/recipes/cuisines/italian.mdx +0 -32
- package/showcases/recipes/cuisines/mexican.mdx +0 -28
- package/showcases/recipes/models.ts +0 -77
- package/showcases/recipes/queries.ts +0 -89
- package/showcases/recipes/recipes/chinese/egg-fried-rice.mdx +0 -43
- package/showcases/recipes/recipes/chinese/mapo-tofu.mdx +0 -47
- package/showcases/recipes/recipes/italian/bruschetta.mdx +0 -38
- package/showcases/recipes/recipes/italian/cacio-e-pepe.mdx +0 -39
- package/showcases/recipes/recipes/italian/tiramisu.mdx +0 -43
- package/showcases/recipes/recipes/mexican/chicken-tinga.mdx +0 -44
- package/showcases/recipes/recipes/mexican/guacamole.mdx +0 -39
- package/showcases/vinyl-collection/albums/bitches-brew.mdx +0 -36
- package/showcases/vinyl-collection/albums/i-put-a-spell-on-you.mdx +0 -35
- package/showcases/vinyl-collection/albums/in-rainbows.mdx +0 -35
- package/showcases/vinyl-collection/albums/kind-of-blue.mdx +0 -32
- package/showcases/vinyl-collection/albums/ok-computer.mdx +0 -37
- package/showcases/vinyl-collection/albums/wild-is-the-wind.mdx +0 -35
- package/showcases/vinyl-collection/artists/miles-davis.mdx +0 -27
- package/showcases/vinyl-collection/artists/nina-simone.mdx +0 -26
- package/showcases/vinyl-collection/artists/radiohead.mdx +0 -27
- package/showcases/vinyl-collection/models.ts +0 -73
- package/showcases/vinyl-collection/queries.ts +0 -87
- package/src/__tests__/semantic-search.integration.test.ts +0 -284
- package/test/ast-query.test.ts +0 -128
- package/test/collection.test.ts +0 -215
- package/test/define-model.test.ts +0 -78
- package/test/document.test.ts +0 -225
- package/test/extract-sections.test.ts +0 -356
- package/test/fixtures/sdlc/MODELS.md +0 -106
- package/test/fixtures/sdlc/SKILL.md +0 -404
- package/test/fixtures/sdlc/epics/authentication.mdx +0 -42
- package/test/fixtures/sdlc/epics/searching-and-browsing.mdx +0 -21
- package/test/fixtures/sdlc/index.ts +0 -9
- package/test/fixtures/sdlc/models.ts +0 -91
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-login.mdx +0 -20
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-register.mdx +0 -20
- package/test/fixtures/sdlc/templates/epic.md +0 -23
- package/test/fixtures/sdlc/templates/story.md +0 -19
- package/test/helpers.ts +0 -21
- package/test/model-instance.test.ts +0 -197
- package/test/pattern.test.ts +0 -191
- package/test/query-dsl.test.ts +0 -431
- package/test/query.test.ts +0 -196
- package/test/relationships.test.ts +0 -214
- package/test/section.test.ts +0 -160
- package/test/table-of-contents.test.ts +0 -135
- package/test/validator.test.ts +0 -62
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -11
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
state: California
|
|
3
|
-
region: west
|
|
4
|
-
established: 1890
|
|
5
|
-
area: "748,436 acres"
|
|
6
|
-
visitors: 3690000
|
|
7
|
-
fee: 35
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Yosemite
|
|
11
|
-
|
|
12
|
-
Granite cliffs, ancient sequoias, and waterfalls that drop thousands of feet. Yosemite Valley alone is worth the trip, but the backcountry — Tuolumne Meadows, the high Sierra — is where the park reveals its full scale.
|
|
13
|
-
|
|
14
|
-
## Wildlife
|
|
15
|
-
|
|
16
|
-
- Black bear
|
|
17
|
-
- Mule deer
|
|
18
|
-
- Peregrine falcon
|
|
19
|
-
- Sierra Nevada bighorn sheep
|
|
20
|
-
- Great gray owl
|
|
21
|
-
- Pacific fisher
|
|
22
|
-
|
|
23
|
-
## Best Seasons
|
|
24
|
-
|
|
25
|
-
| Season | Conditions | Crowds |
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| Spring | Waterfalls at peak flow, some roads closed | Moderate |
|
|
28
|
-
| Summer | Warm, all facilities open | Heavy |
|
|
29
|
-
| Fall | Cool, fewer crowds, fall color | Light |
|
|
30
|
-
| Winter | Snow, Tioga Road closed, skiing | Very light |
|
|
31
|
-
|
|
32
|
-
## Trails
|
|
33
|
-
|
|
34
|
-
### Mist Trail
|
|
35
|
-
|
|
36
|
-
The park's most iconic hike — steep granite stairs through the spray of Vernal Fall and on to Nevada Fall.
|
|
37
|
-
|
|
38
|
-
### Half Dome
|
|
39
|
-
|
|
40
|
-
The bucket-list summit. Cables, permits, and 14+ miles of exertion for a view you'll never forget.
|
|
41
|
-
|
|
42
|
-
### Cathedral Lakes
|
|
43
|
-
|
|
44
|
-
A high-country gem in Tuolumne Meadows — alpine lakes ringed by granite peaks.
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
state: Utah
|
|
3
|
-
region: southwest
|
|
4
|
-
established: 1919
|
|
5
|
-
area: "147,242 acres"
|
|
6
|
-
visitors: 4692000
|
|
7
|
-
fee: 35
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Zion
|
|
11
|
-
|
|
12
|
-
Massive sandstone walls in shades of red, pink, and cream tower above the Virgin River. The canyon is narrow enough to feel intimate and vast enough to feel humbling. Zion manages to be both.
|
|
13
|
-
|
|
14
|
-
## Wildlife
|
|
15
|
-
|
|
16
|
-
- Desert bighorn sheep
|
|
17
|
-
- California condor
|
|
18
|
-
- Ringtail cat
|
|
19
|
-
- Collared lizard
|
|
20
|
-
- Mexican spotted owl
|
|
21
|
-
- Canyon tree frog
|
|
22
|
-
|
|
23
|
-
## Best Seasons
|
|
24
|
-
|
|
25
|
-
| Season | Conditions | Crowds |
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| Spring | Wildflowers, moderate temps, some flash flood risk | Moderate |
|
|
28
|
-
| Summer | Hot (100°F+), best for The Narrows | Very heavy |
|
|
29
|
-
| Fall | Perfect temps, fall color in the canyon | Moderate |
|
|
30
|
-
| Winter | Cool, snow on rims, peaceful | Light |
|
|
31
|
-
|
|
32
|
-
## Trails
|
|
33
|
-
|
|
34
|
-
### Angels Landing
|
|
35
|
-
|
|
36
|
-
A chain-assisted climb along a narrow ridge with 1,500-foot dropoffs on either side. Not for the faint-hearted.
|
|
37
|
-
|
|
38
|
-
### The Narrows
|
|
39
|
-
|
|
40
|
-
Wade upstream through the Virgin River between thousand-foot canyon walls. The ultimate slot canyon experience.
|
|
41
|
-
|
|
42
|
-
### Observation Point
|
|
43
|
-
|
|
44
|
-
The highest viewpoint in the main canyon — looks down on Angels Landing from above.
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example queries for the National Parks showcase.
|
|
3
|
-
*
|
|
4
|
-
* Run with: bun showcases/national-parks/queries.ts
|
|
5
|
-
*/
|
|
6
|
-
import { Collection } from "../../src/index";
|
|
7
|
-
import { Park, Trail } from "./models";
|
|
8
|
-
|
|
9
|
-
const collection = new Collection({
|
|
10
|
-
rootPath: new URL(".", import.meta.url).pathname,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
collection.register(Park);
|
|
14
|
-
collection.register(Trail);
|
|
15
|
-
await collection.load();
|
|
16
|
-
|
|
17
|
-
// ── All parks ──
|
|
18
|
-
const allParks = await collection.query(Park).fetchAll();
|
|
19
|
-
console.log(`Total parks: ${allParks.length}`);
|
|
20
|
-
for (const park of allParks) {
|
|
21
|
-
console.log(
|
|
22
|
-
` ${park.title} (${park.meta.state}) — ${park.meta.area}, est. ${park.meta.established}`
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ── Filter by region ──
|
|
27
|
-
const western = await collection
|
|
28
|
-
.query(Park)
|
|
29
|
-
.whereIn("meta.region", ["west", "southwest"])
|
|
30
|
-
.fetchAll();
|
|
31
|
-
console.log(
|
|
32
|
-
`\nWestern parks: ${western.map((p) => p.title).join(", ")}`
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// ── Popular parks (>3M visitors) ──
|
|
36
|
-
console.log(`\nPopular parks (>3M visitors):`);
|
|
37
|
-
for (const park of allParks) {
|
|
38
|
-
if (park.computed.isPopular) {
|
|
39
|
-
console.log(
|
|
40
|
-
` ${park.title} — ${park.meta.visitors.toLocaleString()} visitors`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ── Park age ──
|
|
46
|
-
console.log(`\nPark ages:`);
|
|
47
|
-
for (const park of allParks) {
|
|
48
|
-
console.log(` ${park.title}: ${park.computed.ageYears} years old`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ── All trails ──
|
|
52
|
-
const allTrails = await collection.query(Trail).fetchAll();
|
|
53
|
-
console.log(`\nTotal trails: ${allTrails.length}`);
|
|
54
|
-
|
|
55
|
-
// ── Filter by difficulty ──
|
|
56
|
-
const strenuous = await collection
|
|
57
|
-
.query(Trail)
|
|
58
|
-
.where("meta.difficulty", "strenuous")
|
|
59
|
-
.fetchAll();
|
|
60
|
-
console.log(
|
|
61
|
-
`\nStrenuous trails: ${strenuous.map((t) => t.title).join(", ")}`
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const easy = await collection
|
|
65
|
-
.query(Trail)
|
|
66
|
-
.where("meta.difficulty", "easy")
|
|
67
|
-
.fetchAll();
|
|
68
|
-
console.log(`Easy trails: ${easy.map((t) => t.title).join(", ")}`);
|
|
69
|
-
|
|
70
|
-
// ── Dog-friendly trails ──
|
|
71
|
-
const dogFriendly = await collection
|
|
72
|
-
.query(Trail)
|
|
73
|
-
.where("meta.dogs", true)
|
|
74
|
-
.fetchAll();
|
|
75
|
-
console.log(
|
|
76
|
-
`\nDog-friendly trails: ${dogFriendly.map((t) => t.title).join(", ")}`
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
// ── Sections as structured data ──
|
|
80
|
-
const yosemite = collection.getModel("parks/yosemite", Park);
|
|
81
|
-
console.log(`\n--- ${yosemite.title} ---`);
|
|
82
|
-
console.log("Wildlife:", yosemite.sections.wildlife);
|
|
83
|
-
console.log("Best seasons:", yosemite.sections.seasons);
|
|
84
|
-
|
|
85
|
-
// ── Park → Trails relationship (hasMany) ──
|
|
86
|
-
const zion = collection.getModel("parks/zion", Park);
|
|
87
|
-
const zionTrails = zion.relationships.trails.fetchAll();
|
|
88
|
-
console.log(
|
|
89
|
-
`\n${zion.title} trails: ${zionTrails.map((t) => t.title).join(", ")}`
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// ── Trail → Park relationship (belongsTo) ──
|
|
93
|
-
const narrows = collection.getModel("trails/the-narrows", Trail);
|
|
94
|
-
const parentPark = narrows.relationships.park.fetch();
|
|
95
|
-
console.log(`\n${narrows.title} is in ${parentPark.title} (${parentPark.meta.state})`);
|
|
96
|
-
console.log("Highlights:", narrows.sections.highlights);
|
|
97
|
-
|
|
98
|
-
// ── Serialize ──
|
|
99
|
-
const json = yosemite.toJSON({
|
|
100
|
-
sections: ["wildlife", "seasons"],
|
|
101
|
-
computed: ["isPopular", "ageYears"],
|
|
102
|
-
});
|
|
103
|
-
console.log(`\nYosemite as JSON:`, JSON.stringify(json, null, 2));
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: zion
|
|
3
|
-
distance: "5.4 mi"
|
|
4
|
-
elevationGain: "1,488 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Angels Landing
|
|
11
|
-
|
|
12
|
-
Zion's most famous trail — and one of the most exposed hikes in any national park. The final half-mile follows a knife-edge ridge with chains for handholds and sheer dropoffs on both sides. Permit required.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Walter's Wiggles — 21 tight switchbacks carved into the cliff
|
|
17
|
-
- Scout Lookout — a wide saddle with stunning views even if you turn back here
|
|
18
|
-
- Chain section along the narrow ridge
|
|
19
|
-
- Panoramic views of Zion Canyon from the 5,790 ft summit
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: yosemite
|
|
3
|
-
distance: "7.0 mi"
|
|
4
|
-
elevationGain: "1,000 ft"
|
|
5
|
-
difficulty: moderate
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Cathedral Lakes
|
|
11
|
-
|
|
12
|
-
A high-country classic in Tuolumne Meadows. Two pristine alpine lakes sit in granite bowls below Cathedral Peak. The trail winds through subalpine meadows dotted with wildflowers in midsummer.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Lower Cathedral Lake — wide and open with Cathedral Peak reflected in the water
|
|
17
|
-
- Upper Cathedral Lake — smaller, more secluded, tucked under the peak
|
|
18
|
-
- Wildflower meadows in July and August
|
|
19
|
-
- Views of Cathedral Peak and Eichorn Pinnacle
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: yosemite
|
|
3
|
-
distance: "14.2 mi"
|
|
4
|
-
elevationGain: "4,800 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Half Dome
|
|
11
|
-
|
|
12
|
-
The defining Yosemite summit. A full-day endurance test that follows the Mist Trail, then climbs through the sub-dome before ascending the final 400 feet on steel cables bolted into granite. Permits required.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Cable section on the final ascent — thrilling and exposed
|
|
17
|
-
- Summit views of Tenaya Canyon, Clouds Rest, and the entire valley
|
|
18
|
-
- Passes both Vernal and Nevada Falls on the way up
|
|
19
|
-
- Sub-dome scramble over smooth granite slabs
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: acadia
|
|
3
|
-
distance: "3.3 mi"
|
|
4
|
-
elevationGain: "105 ft"
|
|
5
|
-
difficulty: easy
|
|
6
|
-
type: loop
|
|
7
|
-
dogs: true
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Jordan Pond Path
|
|
11
|
-
|
|
12
|
-
A flat, family-friendly loop around the clearest lake in Acadia. The path hugs the shoreline through mixed forest, with iconic views of the Bubbles — two rounded granite peaks — reflected in the water.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Mirror-like reflections of the Bubbles on calm mornings
|
|
17
|
-
- Boardwalk sections over the boggy north shore
|
|
18
|
-
- Jordan Pond House for tea and popovers after the hike
|
|
19
|
-
- Accessible for all skill levels and ages
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: yosemite
|
|
3
|
-
distance: "5.4 mi"
|
|
4
|
-
elevationGain: "1,000 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Mist Trail
|
|
11
|
-
|
|
12
|
-
Yosemite's most iconic day hike. Steep granite stairs climb through the spray of Vernal Fall, then continue along the Merced River to the top of Nevada Fall. You will get wet.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Vernal Fall (317 ft) — close enough to feel the mist
|
|
17
|
-
- Nevada Fall (594 ft) — a massive cascade visible from the bridge above
|
|
18
|
-
- Emerald Pool between the two falls
|
|
19
|
-
- Granite staircase carved into the cliff face
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: zion
|
|
3
|
-
distance: "8.0 mi"
|
|
4
|
-
elevationGain: "2,148 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Observation Point
|
|
11
|
-
|
|
12
|
-
The highest viewpoint accessible by trail in Zion Canyon. From the top, you look directly down on Angels Landing — a perspective that makes you appreciate how exposed that ridge really is.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Echo Canyon section — narrow side canyon with weeping rock walls
|
|
17
|
-
- Views down onto Angels Landing and the Great White Throne
|
|
18
|
-
- Cable Mountain and the historic cable works ruins nearby
|
|
19
|
-
- Slickrock terrain near the summit
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: acadia
|
|
3
|
-
distance: "1.6 mi"
|
|
4
|
-
elevationGain: "1,058 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Precipice Trail
|
|
11
|
-
|
|
12
|
-
Acadia's most thrilling route — iron rungs, ladders, and narrow ledges bolted into the cliff face of Champlain Mountain. More via ferrata than hiking trail. Closed spring through midsummer for peregrine falcon nesting.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Iron rung ladders on near-vertical rock faces
|
|
17
|
-
- Exposed traverses with ocean views
|
|
18
|
-
- Summit views of Frenchman Bay and the Porcupine Islands
|
|
19
|
-
- Peregrine falcons nesting on the cliff (visible from below during closures)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
park: zion
|
|
3
|
-
distance: "9.4 mi"
|
|
4
|
-
elevationGain: "334 ft"
|
|
5
|
-
difficulty: strenuous
|
|
6
|
-
type: out-and-back
|
|
7
|
-
dogs: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# The Narrows
|
|
11
|
-
|
|
12
|
-
A river hike through the narrowest section of Zion Canyon. You wade — and occasionally swim — upstream through the Virgin River between sandstone walls up to 1,000 feet high. Entirely unlike any other trail in the park system.
|
|
13
|
-
|
|
14
|
-
## Highlights
|
|
15
|
-
|
|
16
|
-
- Wall Street section — walls narrow to 20 feet with the river filling the entire canyon floor
|
|
17
|
-
- Orderville Canyon junction — a side canyon worth exploring
|
|
18
|
-
- Hanging gardens of ferns and columbine on the canyon walls
|
|
19
|
-
- The interplay of light and shadow in the narrow slot
|
|
@@ -1,28 +0,0 @@
|
|
|
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.
|
|
@@ -1,32 +0,0 @@
|
|
|
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.
|
|
@@ -1,28 +0,0 @@
|
|
|
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.
|
|
@@ -1,77 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
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));
|
|
@@ -1,43 +0,0 @@
|
|
|
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
|