{"@context":"http:\/\/schema.org\/","@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm#product","@type":"ProductGroup","brand":{"@type":"Brand","name":"Muc-Off"},"category":"","description":"The Muc-Off Ultimate Tubeless Kit contains everything needed to get a tubeless-ready wheelset set up and ready to roll. Each Kit contains a Muc-Off Tubeless Rim Tape, Seal Patches, 2 CNC-machined black tubeless Valves and 2 pouches of No Puncture Hassle Tubeless Sealant.\n\nRoad\/gravel\/CX with shallow rims (44mm tubeless valves and 21mmx10m Rim Tape)\nRoad\/gravel\/CX with deep rims (60mm tubeless valves and 21mmx10m Rim Tape)\nXC (44mm tubeless valves and 25mmx10m Rim Tape)\nDH\/Enduro (44mm tubeless valves and 30mmx10m Rim Tape)\nDH Wide (44mm tubeless valves and 35mmx10m Rim Tape)\n","hasVariant":[{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186716898#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416\u0026width=1920","name":"Muc-off Ultimate Tubeless Set-Up Kits - Road 44mm 21mm Tape","offers":{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186716898#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"40.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186716898"},"sku":"20137"},{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186749666#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416\u0026width=1920","name":"Muc-off Ultimate Tubeless Set-Up Kits - XC\/ Gravel 25mm Tape","offers":{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186749666#offer","@type":"Offer","availability":"http:\/\/schema.org\/InStock","price":"40.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186749666"},"sku":"20085"},{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186782434#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.27.48.png?v=1647445416\u0026width=1920","name":"Muc-off Ultimate Tubeless Set-Up Kits - DH\/Trail\/Enduro 30mm","offers":{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186782434#offer","@type":"Offer","availability":"http:\/\/schema.org\/InStock","price":"40.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186782434"},"sku":"20086"},{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186815202#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.07.png?v=1647445416\u0026width=1920","name":"Muc-off Ultimate Tubeless Set-Up Kits - DH \/Plus 35mm Tape","offers":{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186815202#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"40.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186815202"},"sku":"20087"},{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186847970#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.26.png?v=1647445416\u0026width=1920","name":"Muc-off Ultimate Tubeless Set-Up Kits - Long valve Road 60mm 19-21 tape","offers":{"@id":"\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186847970#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"40.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm?variant=42603186847970"},"sku":"20084"}],"name":"Muc-off Ultimate Tubeless Set-Up Kits","productGroupID":"7647592906978","url":"https:\/\/projektride.co.uk\/products\/muc-off-ultimate-tubeless-set-up-dh-trail-enduro-30mm"}
age":{"aspect_ratio":0.884,"height":1220,"width":1078,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":42603186782434,"title":"DH\/Trail\/Enduro 30mm","option1":"DH\/Trail\/Enduro 30mm","option2":null,"option3":null,"sku":"20086","requires_shipping":true,"taxable":true,"featured_image":{"id":36908611109090,"product_id":7647592906978,"position":2,"created_at":"2022-03-16T15:31:33+00:00","updated_at":"2022-03-16T15:43:36+00:00","alt":null,"width":1078,"height":1220,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.27.48.png?v=1647445416","variant_ids":[42603186782434]},"available":true,"name":"Muc-off Ultimate Tubeless Set-Up Kits - DH\/Trail\/Enduro 30mm","public_title":"DH\/Trail\/Enduro 30mm","options":["DH\/Trail\/Enduro 30mm"],"price":4000,"weight":0,"compare_at_price":null,"inventory_management":"shopify","barco:0,"compare_at_price":null,"inventory_management":"shopify","barcode":"","featured_media":{"alt":null,"id":29467157922018,"position":3,"preview_image":{"aspect_ratio":0.884,"height":1220,"width":1078,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.07.png?v=1647445416"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":42603186847970,"title":"Long valve Road 60mm 19-21 tape","option1":"Long valve Road 60mm 19-21 tape","option2":null,"option3":null,"sku":"20084","requires_shipping":true,"taxable":true,"featured_image":{"id":36908611141858,"product_id":7647592906978,"position":4,"created_at":"2022-03-16T15:31:33+00:00","updated_at":"2022-03-16T15:43:36+00:00","alt":null,"width":1078,"height":1220,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.26.png?v=1647445416","variant_ids":[42603186847970]},"available":false,"name":"Muc-off Ultimate Tubeless Set-Up Kits - Long valve Road 60mm 19\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416"],"featured_image":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.43.12.png?v=1647445416","options":["Size"],"media":[{"alt":null,"id":29467228799202,"position":1,"preview_image":{"aspect_ratio":0.801,"height":1088,"width":872,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.43.12.png?v=1647445416"},"aspect_ratio":0.801,"height":1088,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.43.12.png?v=1647445416","width":872},{"alt":null,"id":29467157889250,"position":2,"preview_image":{"aspect_ratio":0.884,"height":1220,"width":1078,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.27.48.png?v=1647445416"},"aspect_ratio":0.884,"height":1220,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.27.48.png?v=1647445416","width":1078},{"alt":null,"id":29467157922018,"\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.36.png?v=1647445416","width":1078},{"alt":null,"id":29467158085858,"position":6,"preview_image":{"aspect_ratio":0.884,"height":1220,"width":1078,"src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416"},"aspect_ratio":0.884,"height":1220,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/products\/Screenshot2022-03-16at15.30.50.png?v=1647445416","width":1078}],"requires_selling_plan":false,"selling_plan_groups":[],"content":"\u003cp\u003e\u003cmeta charset=\"utf-8\"\u003e\u003cspan data-mce-fragment=\"1\"\u003eThe Muc-Off Ultimate Tubeless Kit contains everything needed to get a tubeless-ready wheelset set up and ready to roll. Each Kit contains a Muc-Off Tubeless Rim Tape, Seal Patches, 2 CNC-machined black tubeless Valves and 2 pouches of No Puncture Hassle Tubeless Sealant.\u003c\/span\u003e\u003c\/p\u003e\n\u003cul class=\"description-overview\"\u003e\n\u003cli\u003eRoad\/gravel\/CX with shallow rims (
version>/assets/...
// Trailing digits (e.g. ".../restockrocket-1-521/assets/" -> "521"). Kept numeric to
// match ParseStoqData, so funnel app_version lines up with the order-attribution
// app_version. Reflects the ACTUAL deployed build. This is the SINGLE source of the
// parsed version — preorder.js getAppVersion() reads it back off config rather than
// re-parsing, so the regex lives in exactly one place.
try {
const _stoqVersionMatch = window._RestockRocketConfig.scriptHost.match(/(\d+)\/?(?:assets\/?)?$/);
window._RestockRocketConfig.appVersion = (_stoqVersionMatch && _stoqVersionMatch[1]) || '';
} catch (e) {
window._RestockRocketConfig.appVersion = '';
}
const SETTINGS_CACHE_DURATION = 15 * 60 * 1000; // 15 minutes in milliseconds
const LIQUID_CACHE_MAX_AGE = 15 * 60; // 15 minutes in seconds
// Calculate Liquid cache freshness once at initialization
const liquidRenderedAt = window._RestockRocketConfig.liquidRenderedAt;
// Validate timestamp and calculate ca
<
<
translation data in metafield
// Metafield hasn't been backfilled yet - force refresh
console.debug('STOQ - multi-language enabled but no translation data in cache, fetching fresh');
return null;
}
// Translations object exists in metafield - cache is valid
// If current locale isn't translated, applyTranslations will gracefully use default locale from base fields
if (window._RestockRocketConfig.normalizedLocale &&
!Object.prototype.hasOwnProperty.call(settings.translations, window._RestockRocketConfig.normalizedLocale)) {
console.debug('STOQ - locale not explicitly translated, will use default language from cache');
}
// Don't return null - continue using cache even for untranslated locales
}
const updatedAt = new Date(settings.updated_at);
if (isNaN(updatedAt.getTime())) {
console.debug('STOQ - Invalid updated_at date format in settings');
return null;
}
const
<tomer interaction.
// Detected variants: the variants present in this page's Liquid context (product page has them;
// collection/index/etc. don't expose variants from Liquid). Used to disambiguate "embed didn't
// load" vs "embed loaded but the variant wasn't a preorder/BIS candidate" in order debug.
try {
const _stoqInitConfig = window._RestockRocketConfig;
const _stoqDetectedVariantIds = (_stoqInitConfig.product && Array.isArray(_stoqInitConfig.product.variants))
? _stoqInitConfig.product.variants.map(function(v) { return v.id })
: [];
const _stoqSelectedVariantId = _stoqInitConfig.selected_variant_id;
Shopify?.analytics?.publish?.('stoq_initialized', {
cart_token: _stoqInitConfig.cartToken || '',
page_url: window.location.href,
page_type: _stoqInitConfig.pageType || '',
shop_domain: _stoqInitConfig.shop || '',
market_id: _stoqInitConfig.marketId || '',
detected_variant_ids: _stoqDetectedVariantIds,
selected_variant_id: _stoqSele
ormalizedLocale = window._RestockRocketConfig.normalizedLocale;
const translations = settings.translations;
if (!normalizedLocale) {
// No matching locale has translations; drop payload to save memory
console.debug('STOQ - No matching locale for translations. Available:', Object.keys(translations || {}));
delete settings.translations;
return settings;
}
console.debug(`STOQ - Applying translations for normalized locale: ${normalizedLocale} (original: ${window._RestockRocketConfig.locale})`);
const translatedFields = translations[normalizedLocale];
if (translatedFields && typeof translatedFields === 'object') {
Object.keys(translatedFields).forEach(function(key) {
const value = translatedFields[key];
if (value !== null && value !== undefined && value !== '') {
settings[key] = value;
}
});
} else {
console.debug('STOQ - No translated fields found for locale:', normalizedLd successfully');
} else {
console.debug('STOQ - no cart selling plan updates needed');
}
})
.catch(error => {
console.error('STOQ - error updating cart selling plans:', error);
});
}
});
}
// First try to get settings from metafields with expiry check
const cachedSettings = window._RestockRocketConfig.cachedSettings;
const validCachedSettings = cachedSettings ? checkSettingsExpiry(cachedSettings) : null;
if (validCachedSettings) {
console.debug('STOQ - using cached settings');
initializeScripts(validCachedSettings);
} else {
console.debug('STOQ - fetching fresh settings');
const headers = {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
};
if (window.Shopify?.theme?.role === 'main') {
headers['X-Shopify-Theme-Schema-Name'] = window.Shopify.theme.schema_name;
headers['X-Shopify-Theme-Schech(
`${window._RestockRocketConfig.host}/api/v1/embed/${endpoint}.json`,
{
headers: {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
}
}
)
.then(function(response) {
if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`);
return response.json();
})
.then(function(data) {
try {
apply(data);
} catch (applyError) {
// Apply failures are programming bugs (e.g. response shape changed
// server-side and the assignment threw). Surface them as console.error
// so they're visible in browser logs, then re-throw to fall through
// to the same Liquid-cached fallback as a fetch failure.
console.error('STOQ - apply failed for ' + endpoint + ':', applyError);
throw applyError;
}
})
.catch(function(error) {
console.debug(`STOQ - using cached ${endpoint}:`, error.message);
}ckRocketConfig — the
// bundle re-reads sellingPlans/integrations on every interaction, so the
// late-arriving values benefit subsequent renders even though the first
// paint may use the Liquid-cached values. On any failure the existing
// Liquid-loaded values stay in place via fetchEmbedConfig's catch.
if (!window._RestockRocketConfig.isLiquidCacheFresh && !settings.disable_refresh_on_stale_liquid) {
console.debug('STOQ - Liquid cache stale, refreshing selling_plans + integrations');
Promise.race([
Promise.all([
fetchEmbedConfig('selling_plans', function(data) {
if (data && Array.isArray(data.plans)) {
window._RestockRocketConfig.sellingPlans = data.plans;
window._RestockRocketConfig.disabledSellingPlanIds = data.disabled_plan_ids || [];
}
}),
fetchEmbedConfig('integrations', function(data) {
if (Array.isArray(data)) {
window._RestockRocketConfig.integrations