Enjoy free shipping on all orders over $150, with industry-leading packaging that keeps your food fresh.
Please keep in mind that our eggs already ship for free, so they do not count towards the $150 minimum for meat orders.
Shop with Confidence
We use advanced encryption and trusted payment processors to keep your information safe.
100% Satisfaction Guarantee
We care about your experience and stand by every order, because feeding your family with care means everything to us. If it’s not right, we’ll make it right.
Commitment to Animal Welfare
We use regenerative farming practices that prioritize animal well-being, support healthy ecosystems, and protect the Earth. It’s responsible food that’s good for you, your family, and the planet.
Choosing a selection results in a full page refresh.
Opens in a new window.
y to cap that line at 1 while preserving every other
attempted change (including qty 0 removals and qty 1 sets on other
lines). Returns a Promise when a rewrite happens, or null
when there's nothing to cap (so the caller can run the next
interceptor instead). */
function maybeCapQuantities(input, init, url, opts) {
var attempt = parseAllUpdates(url, opts);
if (!attempt) return null;
return getCart().then(function(cart) {
var items = cart.items || [];
if (attempt.mode === 'change') {
// Find the targeted line.
var line;
if (attempt.changeId) line = items.find(function(it) { return it.key === attempt.changeId; });
else if (attempt.changeLine) line = items[attempt.changeLine - 1];
// Only cap when targeting a herdshare line AND attempting qty > 1.
// Anything else (non-herdshare line, qty 0 removal, qty 1 set) must
// fall through to the removal interceptor so the "remove both"
// paired-removal confirmation flow keeps working.
if (!line || !isHerdshareLine(line) || isNaN(attempt.qty) || attempt.qty <= 1) {
return intercept(input, init, url, opts);
}
var changeBody = { quantity: 1 };
if (attempt.changeId) changeBody.id = attempt.changeId;
else if (attempt.changeLine) changeBody.line = attempt.changeLine;
try { console.info('[WRF Herdshare Cart Guard] capped herdshare /cart/change.js qty to 1'); } catch (e) {}
return origFetch.call(window, url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify(changeBody)
}).then(function(resp) { dispatchCartRefreshSoon(); return resp; });
}
// mode === 'update': walk the entire attempt and cap only herdshare
// lines whose attempted qty exceeds 1; leave every other entry
// (including 0 removals and 1 sets) exactly as the caller asked.
var capped = false;
var newByKey = Object.assign({}, attempt.updates.byKey);
Object.keys(newByKey).forEach(function(k) {
var line = items.find(function(it) { return it.key === k; });
if (line && isHerdshareLine(line) && !isNaN(newByKey[k]) && newByKey[k] > 1) {
newByKey[k] = 1; capped = true;
}
});
var newByLine = attempt.updates.byLine.slice();
newByLine.forEach(function(q, i) {
var line = items[i];
if (line && isHerdshareLine(line) && !isNaN(q) && q > 1) {
newByLine[i] = 1; capped = true;
}
});
// No herdshare line exceeds 1 in this batch — hand off to the
// removal interceptor so qty 0 paired-removal confirmation still
// runs for any update entries setting quantity to 0.
if (!capped) return intercept(input, init, url, opts);
// Shopify's /cart/update.js accepts EITHER an array (positional) OR
// an object keyed by line item key, but not both in the same call.
// We prefer the object form when keys are present; otherwise array.
var updatesPayload;
if (Object.keys(newByKey).length) {
// Mixed mode: fold positional updates into the keyed object using
// the current cart's line keys at those positions so nothing is
// dropped.
newByLine.forEach(function(q, i) {
var line = items[i];
if (line && !Object.prototype.hasOwnProperty.call(newByKey, line.key)) {
newByKey[line.key] = q;
}
});
updatesPayload = newByKey;
} else {
updatesPayload = newByLine;
}
try { console.info('[WRF Herdshare Cart Guard] capped herdshare /cart/update.js qty to 1'); } catch (e) {}
return origFetch.call(window, url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify({ updates: updatesPayload })
}).then(function(resp) { dispatchCartRefreshSoon(); return resp; });
}).catch(function(err) {
console.warn('[WRF Herdshare Cart Guard] qty-cap fell through:', err);
return origFetch.call(window, input, init);
});
}
/* === Intercept fetch === */
var origFetch = window.fetch.bind(window);
window.fetch = function(input, init) {
try {
var url = typeof input === 'string' ? input : (input && input.url) || '';
var opts = init || {};
var method = (opts.method || (input && input.method) || 'GET').toUpperCase();
if (method === 'POST' && /\/cart\/(change|update)(\.js)?(\?|$)/.test(url)) {
// Quantity cap runs first: silently forces herdshare lines to 1.
// It only handles attempts with qty > 1; everything else (incl.
// qty 0 removals) falls through to the removal interceptor below.
var capped = maybeCapQuantities(input, init, url, opts);
if (capped) return capped;
return intercept(input, init, url, opts);
}
} catch (e) { /* fall through */ }
return origFetch.apply(this, arguments);
};
function intercept(input, init, url, opts) {
var removals = parseRemovals(url, opts);
if (!removals.length) return origFetch.call(window, input, init);
return getCart().then(function(cart) {
var hits = watchedLinesBeingRemoved(removals, cart);
if (!hits.length) return origFetch.call(window, input, init);
// Find partner lines for each hit.
var partners = [];
hits.forEach(function(line) {
var vid = String(line.variant_id);
var pair = shareToPair[vid] || membershipToPair[vid];
if (!pair) return;
var partnerVid = (pair.share === vid) ? pair.membership : pair.share;
var partnerLine = findLineByVariant(cart, partnerVid);
if (partnerLine) partners.push({ removedLine: line, partnerLine: partnerLine, pair: pair });
});
// If no partner is actually in cart (e.g. Appstle auto-injection failed)
// there's nothing to keep linked, so let the removal proceed normally.
if (!partners.length) return origFetch.call(window, input, init);
return showConfirm(buildMessage(hits, cart), 'Remove both', 'Keep both').then(function(ok) {
if (!ok) {
// Synthesize a 200 OK with the current (unchanged) cart so the
// theme's cart UI re-renders without applying the removal.
var snapshot = JSON.stringify(cart);
dispatchCartRefreshSoon();
return new Response(snapshot, {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
// Accept: do the original removal first, then chain partner removals.
return origFetch.call(window, input, init).then(function(originalResponse) {
return partners.reduce(function(p, item) {
return p.then(function() { return removeByKey(item.partnerLine.key); });
}, Promise.resolve()).then(function() {
dispatchCartRefreshSoon();
return originalResponse;
});
});
});
}).catch(function(err) {
console.warn('[WRF Herdshare Cart Guard] fell through to original fetch:', err);
return origFetch.call(window, input, init);
});
}
/* Many themes listen for these events to re-render cart drawer/mini-cart.
We dispatch all the common ones and, as a final fallback, refresh the
visible cart page so the user always sees the post-removal state. */
function dispatchCartRefreshSoon() {
setTimeout(function() {
['cart:refresh','cart:updated','cart:change','cart:build','ajaxCart.afterCartLoad']
.forEach(function(name) {
try { document.dispatchEvent(new CustomEvent(name, { bubbles: true })); } catch (e) {}
});
if (/^\/cart(\/|$|\?)/.test(window.location.pathname + window.location.search)) {
setTimeout(function() { window.location.reload(); }, 250);
}
}, 50);
}
})();