feat(catalog): migrate dev seed data to manufacturer-slug-based global items
Replace brand text field with manufacturerSlug in DEV_GLOBAL_ITEMS, global-items-seed.json, and seed-global-items.ts. Add DEV_MANUFACTURERS for dev-only brands not in SEED_MANUFACTURERS. Expand SEED_MANUFACTURERS with 8 additional manufacturers referenced by seed JSON (Nemo, Therm-a-Rest, Toaks, Katadyn, HydraPak, Nitecore, Outdoor Research, Exposure Lights). Update dev-seed.ts to resolve slug→id before insert and use manufacturerId as the deduplication key. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,13 +17,34 @@ export const DEV_CATEGORIES = [
|
||||
{ name: "Navigation", icon: "compass" },
|
||||
] as const;
|
||||
|
||||
// ── Manufacturers ──────────────────────────────────────────────────
|
||||
// Seeded with onConflictDoNothing — safe to overlap with SEED_MANUFACTURERS.
|
||||
|
||||
export const DEV_MANUFACTURERS = [
|
||||
{ name: "Rockgeist", slug: "rockgeist", website: "https://rockgeist.com", country: "US", tier: 1 },
|
||||
{ name: "Oveja Negra", slug: "oveja-negra", website: "https://ovejanegrabikewear.com", country: "US", tier: 1 },
|
||||
{ name: "Durston", slug: "durston", website: "https://durstondesigns.com", country: "US", tier: 1 },
|
||||
{ name: "Enlightened Equipment", slug: "enlightened-equipment", website: "https://enlightenedequipment.com", country: "US", tier: 1 },
|
||||
{ name: "BRS", slug: "brs", website: "https://brs-outdoor.com", country: "CN", tier: 1 },
|
||||
{ name: "Soto", slug: "soto", website: "https://sotostoves.com", country: "JP", tier: 1 },
|
||||
{ name: "Snow Peak", slug: "snow-peak", website: "https://snowpeak.com", country: "JP", tier: 1 },
|
||||
{ name: "Lezyne", slug: "lezyne", website: "https://lezyne.com", country: "US", tier: 1 },
|
||||
{ name: "Fenix", slug: "fenix", website: "https://fenixlighting.com", country: "CN", tier: 1 },
|
||||
{ name: "Park Tool", slug: "park-tool", website: "https://parktool.com", country: "US", tier: 1 },
|
||||
{ name: "Gorilla Tape", slug: "gorilla-tape", website: "https://gorillatough.com", country: "US", tier: 1 },
|
||||
{ name: "Patagonia", slug: "patagonia", website: "https://patagonia.com", country: "US", tier: 1 },
|
||||
{ name: "Frogg Toggs", slug: "frogg-toggs", website: "https://froggtoggs.com", country: "US", tier: 1 },
|
||||
{ name: "Buff", slug: "buff", website: "https://buffwear.com", country: "ES", tier: 1 },
|
||||
{ name: "Anker", slug: "anker", website: "https://anker.com", country: "CN", tier: 1 },
|
||||
] as const;
|
||||
|
||||
// ── Global Items ───────────────────────────────────────────────────
|
||||
// Index positions are referenced by user items, thread candidates, and tag assignments.
|
||||
|
||||
export const DEV_GLOBAL_ITEMS = [
|
||||
// Bags (indices 0-5)
|
||||
{
|
||||
brand: "Revelate Designs",
|
||||
manufacturerSlug: "revelate-designs",
|
||||
model: "Terrapin System",
|
||||
category: "bags",
|
||||
weightGrams: 529,
|
||||
@@ -32,7 +53,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Waterproof saddle bag with 14L capacity, roll-top closure, and integrated seat bag mount.",
|
||||
},
|
||||
{
|
||||
brand: "Apidura",
|
||||
manufacturerSlug: "apidura",
|
||||
model: "Expedition Handlebar Pack",
|
||||
category: "bags",
|
||||
weightGrams: 300,
|
||||
@@ -41,7 +62,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"14L waterproof handlebar roll bag with internal dry bag and accessory pocket.",
|
||||
},
|
||||
{
|
||||
brand: "Ortlieb",
|
||||
manufacturerSlug: "ortlieb",
|
||||
model: "Frame-Pack RC",
|
||||
category: "bags",
|
||||
weightGrams: 250,
|
||||
@@ -50,7 +71,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"6L waterproof roll-closure frame bag with TIZIP zipper for full-frame bikes.",
|
||||
},
|
||||
{
|
||||
brand: "Rockgeist",
|
||||
manufacturerSlug: "rockgeist",
|
||||
model: "BarJam",
|
||||
category: "bags",
|
||||
weightGrams: 142,
|
||||
@@ -59,7 +80,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Ultralight handlebar harness with side-loading dry bag compatibility.",
|
||||
},
|
||||
{
|
||||
brand: "Oveja Negra",
|
||||
manufacturerSlug: "oveja-negra",
|
||||
model: "Superwedgie",
|
||||
category: "bags",
|
||||
weightGrams: 170,
|
||||
@@ -68,7 +89,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Half-frame bag with easy-access zipper and internal organization.",
|
||||
},
|
||||
{
|
||||
brand: "Apidura",
|
||||
manufacturerSlug: "apidura",
|
||||
model: "Racing Top Tube Pack",
|
||||
category: "bags",
|
||||
weightGrams: 72,
|
||||
@@ -79,7 +100,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Shelter (indices 6-9)
|
||||
{
|
||||
brand: "Zpacks",
|
||||
manufacturerSlug: "zpacks",
|
||||
model: "Duplex",
|
||||
category: "shelter",
|
||||
weightGrams: 539,
|
||||
@@ -88,7 +109,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Dyneema Composite Fabric two-person trekking pole shelter, freestanding with optional poles.",
|
||||
},
|
||||
{
|
||||
brand: "Tarptent",
|
||||
manufacturerSlug: "tarptent",
|
||||
model: "Stratospire Li",
|
||||
category: "shelter",
|
||||
weightGrams: 737,
|
||||
@@ -97,7 +118,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Two-person double-wall tent in Dyneema with dual vestibules and excellent ventilation.",
|
||||
},
|
||||
{
|
||||
brand: "Durston",
|
||||
manufacturerSlug: "durston",
|
||||
model: "X-Mid 1 Solid",
|
||||
category: "shelter",
|
||||
weightGrams: 880,
|
||||
@@ -106,7 +127,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Single-wall silpoly trekking pole tent with symmetrical design and two vestibules.",
|
||||
},
|
||||
{
|
||||
brand: "Big Agnes",
|
||||
manufacturerSlug: "big-agnes",
|
||||
model: "Copper Spur HV UL1",
|
||||
category: "shelter",
|
||||
weightGrams: 936,
|
||||
@@ -117,7 +138,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Sleep System (indices 10-14)
|
||||
{
|
||||
brand: "Enlightened Equipment",
|
||||
manufacturerSlug: "enlightened-equipment",
|
||||
model: "Enigma 20F",
|
||||
category: "sleep",
|
||||
weightGrams: 567,
|
||||
@@ -126,7 +147,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"20F down quilt with 850FP DownTek water-resistant fill, sewn footbox option.",
|
||||
},
|
||||
{
|
||||
brand: "Therm-a-Rest",
|
||||
manufacturerSlug: "therm-a-rest",
|
||||
model: "NeoAir XLite NXT",
|
||||
category: "sleep",
|
||||
weightGrams: 354,
|
||||
@@ -135,7 +156,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"R-value 4.5 ultralight inflatable sleeping pad with ThermaCapture reflective technology.",
|
||||
},
|
||||
{
|
||||
brand: "Nemo",
|
||||
manufacturerSlug: "nemo",
|
||||
model: "Tensor Insulated Regular",
|
||||
category: "sleep",
|
||||
weightGrams: 425,
|
||||
@@ -144,7 +165,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"R-value 4.2 insulated sleeping pad with Spaceframe baffles for stability.",
|
||||
},
|
||||
{
|
||||
brand: "Sea to Summit",
|
||||
manufacturerSlug: "sea-to-summit",
|
||||
model: "Aeros Premium Pillow",
|
||||
category: "sleep",
|
||||
weightGrams: 79,
|
||||
@@ -153,7 +174,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Brushed 50D polyester inflatable pillow with multifunctional valve.",
|
||||
},
|
||||
{
|
||||
brand: "Western Mountaineering",
|
||||
manufacturerSlug: "western-mountaineering",
|
||||
model: "NanoLite 22F",
|
||||
category: "sleep",
|
||||
weightGrams: 510,
|
||||
@@ -164,7 +185,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Cooking (indices 15-19)
|
||||
{
|
||||
brand: "BRS",
|
||||
manufacturerSlug: "brs",
|
||||
model: "BRS-3000T",
|
||||
category: "cooking",
|
||||
weightGrams: 25,
|
||||
@@ -173,7 +194,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Ultralight titanium canister stove, 25g with piezo ignition, 2700W output.",
|
||||
},
|
||||
{
|
||||
brand: "Soto",
|
||||
manufacturerSlug: "soto",
|
||||
model: "WindMaster",
|
||||
category: "cooking",
|
||||
weightGrams: 67,
|
||||
@@ -182,7 +203,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Micro-regulator stove with concave burner head for excellent wind resistance.",
|
||||
},
|
||||
{
|
||||
brand: "Toaks",
|
||||
manufacturerSlug: "toaks",
|
||||
model: "Light Titanium 750ml",
|
||||
category: "cooking",
|
||||
weightGrams: 86,
|
||||
@@ -191,7 +212,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Titanium pot with graduated measurements, lid, and folding bail handle.",
|
||||
},
|
||||
{
|
||||
brand: "Snow Peak",
|
||||
manufacturerSlug: "snow-peak",
|
||||
model: "Ti-Mini Solo Combo",
|
||||
category: "cooking",
|
||||
weightGrams: 198,
|
||||
@@ -200,7 +221,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Titanium cookset with 850ml pot, lid/pan, and nesting mug for solo cooking.",
|
||||
},
|
||||
{
|
||||
brand: "MSR",
|
||||
manufacturerSlug: "msr",
|
||||
model: "PocketRocket Deluxe",
|
||||
category: "cooking",
|
||||
weightGrams: 83,
|
||||
@@ -211,7 +232,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Lighting (indices 20-22)
|
||||
{
|
||||
brand: "Nitecore",
|
||||
manufacturerSlug: "nitecore",
|
||||
model: "NU25 UL",
|
||||
category: "lighting",
|
||||
weightGrams: 28,
|
||||
@@ -220,7 +241,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Rechargeable ultralight headlamp with 400 lumens, red/high-CRI aux LEDs.",
|
||||
},
|
||||
{
|
||||
brand: "Lezyne",
|
||||
manufacturerSlug: "lezyne",
|
||||
model: "Lite Drive 1200+",
|
||||
category: "lighting",
|
||||
weightGrams: 176,
|
||||
@@ -229,7 +250,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"1200 lumen USB-C rechargeable bike light with MOR optical lens design.",
|
||||
},
|
||||
{
|
||||
brand: "Fenix",
|
||||
manufacturerSlug: "fenix",
|
||||
model: "HL60R",
|
||||
category: "lighting",
|
||||
weightGrams: 134,
|
||||
@@ -240,7 +261,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Tools & Repair (indices 23-25)
|
||||
{
|
||||
brand: "Park Tool",
|
||||
manufacturerSlug: "park-tool",
|
||||
model: "IB-3",
|
||||
category: "tools",
|
||||
weightGrams: 175,
|
||||
@@ -249,7 +270,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Folding hex/Torx multi-tool with 3-6mm hex, T25, Phillips and flathead.",
|
||||
},
|
||||
{
|
||||
brand: "Lezyne",
|
||||
manufacturerSlug: "lezyne",
|
||||
model: "CNC Chain Breaker",
|
||||
category: "tools",
|
||||
weightGrams: 28,
|
||||
@@ -258,7 +279,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"CNC-machined aluminum chain tool compatible with 8-12 speed chains.",
|
||||
},
|
||||
{
|
||||
brand: "Gorilla Tape",
|
||||
manufacturerSlug: "gorilla-tape",
|
||||
model: "Mini Duct Tape Roll",
|
||||
category: "tools",
|
||||
weightGrams: 30,
|
||||
@@ -268,7 +289,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Clothing (indices 26-28)
|
||||
{
|
||||
brand: "Patagonia",
|
||||
manufacturerSlug: "patagonia",
|
||||
model: "R1 Air Full-Zip",
|
||||
category: "clothing",
|
||||
weightGrams: 266,
|
||||
@@ -277,7 +298,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Breathable midlayer fleece with open-knit R1 Air fabric for high-output activities.",
|
||||
},
|
||||
{
|
||||
brand: "Frogg Toggs",
|
||||
manufacturerSlug: "frogg-toggs",
|
||||
model: "Ultra-Lite2 Rain Suit",
|
||||
category: "clothing",
|
||||
weightGrams: 340,
|
||||
@@ -286,7 +307,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"Budget ultralight rain jacket and pants set, DriPore breathable material.",
|
||||
},
|
||||
{
|
||||
brand: "Buff",
|
||||
manufacturerSlug: "buff",
|
||||
model: "Merino Wool Multifunctional",
|
||||
category: "clothing",
|
||||
weightGrams: 43,
|
||||
@@ -297,7 +318,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Water (indices 29-31)
|
||||
{
|
||||
brand: "Sawyer",
|
||||
manufacturerSlug: "sawyer",
|
||||
model: "Squeeze SP129",
|
||||
category: "water",
|
||||
weightGrams: 85,
|
||||
@@ -306,7 +327,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"0.1 micron hollow-fiber water filter with high flow rate and backflush capability.",
|
||||
},
|
||||
{
|
||||
brand: "Katadyn",
|
||||
manufacturerSlug: "katadyn",
|
||||
model: "BeFree 1L",
|
||||
category: "water",
|
||||
weightGrams: 63,
|
||||
@@ -315,7 +336,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"EZ-Clean membrane filter with collapsible Hydrapak flask, 2L/min flow rate.",
|
||||
},
|
||||
{
|
||||
brand: "HydraPak",
|
||||
manufacturerSlug: "hydrapak",
|
||||
model: "Seeker 2L",
|
||||
category: "water",
|
||||
weightGrams: 76,
|
||||
@@ -326,7 +347,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Electronics (indices 32-33)
|
||||
{
|
||||
brand: "Anker",
|
||||
manufacturerSlug: "anker",
|
||||
model: "Nano Power Bank 10000 PD",
|
||||
category: "electronics",
|
||||
weightGrams: 220,
|
||||
@@ -335,7 +356,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"10000mAh 30W USB-C PD power bank with built-in display and passthrough charging.",
|
||||
},
|
||||
{
|
||||
brand: "Garmin",
|
||||
manufacturerSlug: "garmin",
|
||||
model: "inReach Mini 2",
|
||||
category: "electronics",
|
||||
weightGrams: 100,
|
||||
@@ -346,7 +367,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
|
||||
// Navigation (indices 34-35)
|
||||
{
|
||||
brand: "Wahoo",
|
||||
manufacturerSlug: "wahoo",
|
||||
model: "ELEMNT BOLT V2",
|
||||
category: "navigation",
|
||||
weightGrams: 68,
|
||||
@@ -355,7 +376,7 @@ export const DEV_GLOBAL_ITEMS = [
|
||||
"GPS cycling computer with color display, turn-by-turn navigation, and smart trainer integration.",
|
||||
},
|
||||
{
|
||||
brand: "Ortlieb",
|
||||
manufacturerSlug: "ortlieb",
|
||||
model: "Ultimate Six Classic",
|
||||
category: "navigation",
|
||||
weightGrams: 500,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { and, eq, like, sql } from "drizzle-orm";
|
||||
import {
|
||||
DEV_CATEGORIES,
|
||||
DEV_GLOBAL_ITEMS,
|
||||
DEV_MANUFACTURERS,
|
||||
DEV_MARKET_PRICES,
|
||||
DEV_SETTINGS,
|
||||
DEV_SETUPS,
|
||||
@@ -79,9 +80,12 @@ async function seedDevData(database: Db = db) {
|
||||
await clearDevData(database);
|
||||
|
||||
try {
|
||||
// ── 1. Seed global items and tags ──────────────────────────
|
||||
// ── 1. Seed global items, tags, and dev-specific manufacturers ─
|
||||
await seedGlobalItems(database);
|
||||
console.log(" Global items and tags seeded.");
|
||||
for (const m of DEV_MANUFACTURERS) {
|
||||
await database.insert(schema.manufacturers).values(m).onConflictDoNothing();
|
||||
}
|
||||
console.log(" Global items, tags, and manufacturers seeded.");
|
||||
|
||||
// ── 2. Insert dev user ─────────────────────────────────────
|
||||
const [user] = await database
|
||||
@@ -123,20 +127,29 @@ async function seedDevData(database: Db = db) {
|
||||
|
||||
// ── 5. Insert global items and tag assignments ─────────────
|
||||
// DEV_GLOBAL_ITEMS may overlap with seed-global-items.json entries.
|
||||
// Insert only items that don't already exist (by brand+model).
|
||||
// Insert only items that don't already exist (by manufacturerId+model).
|
||||
const allManufacturers = await database.select().from(schema.manufacturers);
|
||||
const mfBySlug = new Map(allManufacturers.map((m) => [m.slug, m.id]));
|
||||
|
||||
const existingGlobalItems = await database
|
||||
.select()
|
||||
.from(schema.globalItems);
|
||||
const existingGlobalItemMap = new Map<string, number>();
|
||||
for (const gi of existingGlobalItems) {
|
||||
existingGlobalItemMap.set(`${gi.brand}::${gi.model}`, gi.id);
|
||||
existingGlobalItemMap.set(`${gi.manufacturerId}::${gi.model}`, gi.id);
|
||||
}
|
||||
|
||||
const globalItemIds: number[] = [];
|
||||
let newGlobalCount = 0;
|
||||
|
||||
for (const item of DEV_GLOBAL_ITEMS) {
|
||||
const key = `${item.brand}::${item.model}`;
|
||||
const mfId = mfBySlug.get(item.manufacturerSlug);
|
||||
if (!mfId) {
|
||||
console.warn(` Skipping "${item.model}" — unknown manufacturer slug: ${item.manufacturerSlug}`);
|
||||
globalItemIds.push(0); // placeholder to keep index alignment
|
||||
continue;
|
||||
}
|
||||
const key = `${mfId}::${item.model}`;
|
||||
const existingId = existingGlobalItemMap.get(key);
|
||||
if (existingId) {
|
||||
globalItemIds.push(existingId);
|
||||
@@ -144,7 +157,7 @@ async function seedDevData(database: Db = db) {
|
||||
const [inserted] = await database
|
||||
.insert(schema.globalItems)
|
||||
.values({
|
||||
brand: item.brand,
|
||||
manufacturerId: mfId,
|
||||
model: item.model,
|
||||
category: item.category,
|
||||
weightGrams: item.weightGrams,
|
||||
@@ -154,7 +167,7 @@ async function seedDevData(database: Db = db) {
|
||||
.returning();
|
||||
if (!inserted)
|
||||
throw new Error(
|
||||
`Failed to insert global item: ${item.brand} ${item.model}`,
|
||||
`Failed to insert global item: ${item.manufacturerSlug} ${item.model}`,
|
||||
);
|
||||
globalItemIds.push(inserted.id);
|
||||
newGlobalCount++;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"brand": "Revelate Designs",
|
||||
"manufacturerSlug": "revelate-designs",
|
||||
"model": "Terrapin System",
|
||||
"category": "bags",
|
||||
"weightGrams": 529,
|
||||
@@ -8,7 +8,7 @@
|
||||
"description": "Waterproof saddle bag with 14L capacity, roll-top closure, and integrated Revelate seat bag mount."
|
||||
},
|
||||
{
|
||||
"brand": "Apidura",
|
||||
"manufacturerSlug": "apidura",
|
||||
"model": "Expedition Handlebar Pack",
|
||||
"category": "bags",
|
||||
"weightGrams": 300,
|
||||
@@ -16,7 +16,7 @@
|
||||
"description": "14L waterproof handlebar roll bag with internal dry bag and accessory pocket."
|
||||
},
|
||||
{
|
||||
"brand": "Ortlieb",
|
||||
"manufacturerSlug": "ortlieb",
|
||||
"model": "Frame-Pack Toptube",
|
||||
"category": "bags",
|
||||
"weightGrams": 180,
|
||||
@@ -24,7 +24,7 @@
|
||||
"description": "4L waterproof top-tube bag with magnetic closure and reflective details."
|
||||
},
|
||||
{
|
||||
"brand": "Revelate Designs",
|
||||
"manufacturerSlug": "revelate-designs",
|
||||
"model": "Tangle Frame Bag",
|
||||
"category": "bags",
|
||||
"weightGrams": 170,
|
||||
@@ -32,7 +32,7 @@
|
||||
"description": "Full-frame bag with water-resistant construction and multiple internal pockets."
|
||||
},
|
||||
{
|
||||
"brand": "Big Agnes",
|
||||
"manufacturerSlug": "big-agnes",
|
||||
"model": "Copper Spur HV UL1",
|
||||
"category": "shelters",
|
||||
"weightGrams": 879,
|
||||
@@ -40,7 +40,7 @@
|
||||
"description": "Ultralight 1-person freestanding tent with high-volume hub design and DAC Featherlite poles."
|
||||
},
|
||||
{
|
||||
"brand": "Tarptent",
|
||||
"manufacturerSlug": "tarptent",
|
||||
"model": "Protrail Li",
|
||||
"category": "shelters",
|
||||
"weightGrams": 454,
|
||||
@@ -48,7 +48,7 @@
|
||||
"description": "Ultralight single-wall trekking pole shelter in Dyneema composite fabric."
|
||||
},
|
||||
{
|
||||
"brand": "Outdoor Research",
|
||||
"manufacturerSlug": "outdoor-research",
|
||||
"model": "Helium Bivy",
|
||||
"category": "shelters",
|
||||
"weightGrams": 510,
|
||||
@@ -56,7 +56,7 @@
|
||||
"description": "Waterproof breathable bivy sack with single-hoop pole and full-zip entry."
|
||||
},
|
||||
{
|
||||
"brand": "Sea to Summit",
|
||||
"manufacturerSlug": "sea-to-summit",
|
||||
"model": "Spark SP1",
|
||||
"category": "sleep-systems",
|
||||
"weightGrams": 375,
|
||||
@@ -64,7 +64,7 @@
|
||||
"description": "Ultralight 850+ fill down sleeping bag rated to 40F/4C with Ultra-Dry Down."
|
||||
},
|
||||
{
|
||||
"brand": "Nemo",
|
||||
"manufacturerSlug": "nemo",
|
||||
"model": "Tensor Ultralight Insulated Regular",
|
||||
"category": "sleep-systems",
|
||||
"weightGrams": 425,
|
||||
@@ -72,7 +72,7 @@
|
||||
"description": "3-inch thick insulated sleeping pad with R-value 4.2 and Spaceframe baffles."
|
||||
},
|
||||
{
|
||||
"brand": "Therm-a-Rest",
|
||||
"manufacturerSlug": "therm-a-rest",
|
||||
"model": "NeoAir XLite NXT",
|
||||
"category": "sleep-systems",
|
||||
"weightGrams": 354,
|
||||
@@ -80,7 +80,7 @@
|
||||
"description": "Ultralight insulated air pad with R-value 4.5, Triangular Core Matrix, and WingLock valve."
|
||||
},
|
||||
{
|
||||
"brand": "MSR",
|
||||
"manufacturerSlug": "msr",
|
||||
"model": "PocketRocket 2",
|
||||
"category": "cooking",
|
||||
"weightGrams": 73,
|
||||
@@ -88,7 +88,7 @@
|
||||
"description": "Ultralight canister stove with adjustable flame control, boils 1L in 3.5 minutes."
|
||||
},
|
||||
{
|
||||
"brand": "Toaks",
|
||||
"manufacturerSlug": "toaks",
|
||||
"model": "Titanium 750ml Pot",
|
||||
"category": "cooking",
|
||||
"weightGrams": 103,
|
||||
@@ -96,7 +96,7 @@
|
||||
"description": "Ultralight titanium pot with lid and foldable handles, 750ml capacity."
|
||||
},
|
||||
{
|
||||
"brand": "Katadyn",
|
||||
"manufacturerSlug": "katadyn",
|
||||
"model": "BeFree 1.0L",
|
||||
"category": "hydration",
|
||||
"weightGrams": 59,
|
||||
@@ -104,7 +104,7 @@
|
||||
"description": "Ultralight hollow fiber water filter with 0.1 micron filtration and 1L soft flask."
|
||||
},
|
||||
{
|
||||
"brand": "HydraPak",
|
||||
"manufacturerSlug": "hydrapak",
|
||||
"model": "Seeker 2L",
|
||||
"category": "hydration",
|
||||
"weightGrams": 73,
|
||||
@@ -112,7 +112,7 @@
|
||||
"description": "Collapsible 2L water storage with wide mouth and compatible with Katadyn BeFree filter."
|
||||
},
|
||||
{
|
||||
"brand": "Nitecore",
|
||||
"manufacturerSlug": "nitecore",
|
||||
"model": "NU25 UL",
|
||||
"category": "lighting",
|
||||
"weightGrams": 28,
|
||||
@@ -120,7 +120,7 @@
|
||||
"description": "Ultralight USB-C rechargeable headlamp with 400 lumens max and red light mode."
|
||||
},
|
||||
{
|
||||
"brand": "Exposure Lights",
|
||||
"manufacturerSlug": "exposure-lights",
|
||||
"model": "Revo Dynamo",
|
||||
"category": "lighting",
|
||||
"weightGrams": 130,
|
||||
@@ -128,7 +128,7 @@
|
||||
"description": "Dynamo-powered front light with 800 lumens, built-in standlight, and USB charging output."
|
||||
},
|
||||
{
|
||||
"brand": "Surly",
|
||||
"manufacturerSlug": "surly",
|
||||
"model": "24-Pack Rack",
|
||||
"category": "racks",
|
||||
"weightGrams": 750,
|
||||
@@ -136,7 +136,7 @@
|
||||
"description": "Front rack for bikepacking with 24-pack platform, fits most forks with mid-blade eyelets."
|
||||
},
|
||||
{
|
||||
"brand": "Salsa",
|
||||
"manufacturerSlug": "salsa-cycles",
|
||||
"model": "Anything Cage HD",
|
||||
"category": "accessories",
|
||||
"weightGrams": 80,
|
||||
|
||||
@@ -25,6 +25,15 @@ export const SEED_MANUFACTURERS = [
|
||||
{ name: "Trek", slug: "trek", website: "https://trekbikes.com", country: "US", tier: 1 },
|
||||
{ name: "Salsa Cycles", slug: "salsa-cycles", website: "https://salsacycles.com", country: "US", tier: 1 },
|
||||
{ name: "Surly", slug: "surly", website: "https://surlybikes.com", country: "US", tier: 1 },
|
||||
// Additional manufacturers referenced in seed data
|
||||
{ name: "Nemo", slug: "nemo", website: "https://nemoequipment.com", country: "US", tier: 1 },
|
||||
{ name: "Therm-a-Rest", slug: "therm-a-rest", website: "https://thermarest.com", country: "US", tier: 1 },
|
||||
{ name: "Toaks", slug: "toaks", website: "https://toaksoutdoor.com", country: "CN", tier: 1 },
|
||||
{ name: "Katadyn", slug: "katadyn", website: "https://katadyn.com", country: "CH", tier: 1 },
|
||||
{ name: "HydraPak", slug: "hydrapak", website: "https://hydrapak.com", country: "US", tier: 1 },
|
||||
{ name: "Nitecore", slug: "nitecore", website: "https://nitecore.com", country: "CN", tier: 1 },
|
||||
{ name: "Outdoor Research", slug: "outdoor-research", website: "https://outdoorresearch.com", country: "US", tier: 1 },
|
||||
{ name: "Exposure Lights", slug: "exposure-lights", website: "https://exposurelights.com", country: "GB", tier: 1 },
|
||||
];
|
||||
|
||||
const SEED_TAGS = [
|
||||
@@ -68,10 +77,10 @@ export async function seedGlobalItems(db: Db = prodDb) {
|
||||
if (existing.length > 0) return;
|
||||
|
||||
const allManufacturers = await db.select().from(manufacturers);
|
||||
const mfByName = new Map(allManufacturers.map((m) => [m.name, m.id]));
|
||||
const mfBySlug = new Map(allManufacturers.map((m) => [m.slug, m.id]));
|
||||
|
||||
for (const item of seedData) {
|
||||
const manufacturerId = mfByName.get(item.brand);
|
||||
const manufacturerId = mfBySlug.get(item.manufacturerSlug);
|
||||
if (!manufacturerId) continue;
|
||||
|
||||
await db.insert(globalItems).values({
|
||||
|
||||
Reference in New Issue
Block a user