UNPKG

@sane-shopify/sync-utils

Version:

Syncing utility for Node & the browser

1,153 lines (1,139 loc) 158 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var unwindEdges = require('@good-idea/unwind-edges'); var createSanityClient = _interopDefault(require('@sanity/client')); var PQueue = _interopDefault(require('p-queue')); var xstate = require('xstate'); var types = require('@sane-shopify/types'); var Debug = _interopDefault(require('debug')); var fetch = _interopDefault(require('cross-fetch')); var LeakyBucket = _interopDefault(require('@good-idea/leaky-bucket')); var gql = _interopDefault(require('graphql-tag')); var deepMerge = _interopDefault(require('deepmerge')); var lodash = require('lodash'); function _regeneratorRuntime() { _regeneratorRuntime = function () { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function (obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == typeof value && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function (method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator.return && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function (skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); }, stop: function () { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function (exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function (type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function (record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function (finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, catch: function (tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function (iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; } var _on, _on2, _on3, _on4, _on5, _on6, _states; var initialContext = { documentsFetched: [], toSync: [], syncOperations: [], toLink: [], linkOperations: [], error: undefined, errorMessage: undefined, valid: false, ready: false, shopName: undefined }; var syncMachine = /*#__PURE__*/xstate.Machine({ id: 'syncMachine', initial: types.SyncStates.INIT, context: initialContext, states: (_states = {}, _states[types.SyncStates.INIT] = { on: (_on = {}, _on[types.SyncEventType.Valid] = { target: types.SyncStates.READY, actions: ['onReady'] }, _on[types.SyncEventType.Invalid] = { target: types.SyncStates.SETUP, actions: ['onSetup'] }, _on) }, _states[types.SyncStates.SETUP] = { on: (_on2 = {}, _on2[types.SyncEventType.Valid] = { target: types.SyncStates.READY, actions: ['onReady'] }, _on2[types.SyncEventType.Invalid] = { target: types.SyncStates.SETUP, actions: ['onError'] }, _on2) }, _states[types.SyncStates.READY] = { on: (_on3 = {}, _on3[types.SyncEventType.Sync] = types.SyncStates.SYNCING, _on3[types.SyncEventType.ClearedSecrets] = { target: types.SyncStates.SETUP, actions: 'reset' }, _on3) }, _states[types.SyncStates.SYNCING] = { on: (_on4 = {}, _on4[types.SyncEventType.DocumentsFetched] = { internal: true, actions: ['onDocumentsFetched'] }, _on4[types.SyncEventType.FetchComplete] = { internal: true, actions: ['onFetchedComplete'] }, _on4[types.SyncEventType.DocumentsSynced] = { internal: true, actions: ['onDocumentsSynced'] }, _on4[types.SyncEventType.DocumentsLinked] = { internal: true, actions: ['onDocumentLinked'] }, _on4[types.SyncEventType.Complete] = types.SyncStates.COMPLETE, _on4[types.SyncEventType.Errored] = { target: types.SyncStates.SYNC_ERROR, actions: ['onError'] }, _on4) }, _states[types.SyncStates.COMPLETE] = { on: (_on5 = {}, _on5[types.SyncEventType.Reset] = types.SyncStates.READY, _on5) }, _states[types.SyncStates.SYNC_ERROR] = { on: (_on6 = {}, _on6[types.SyncEventType.Reset] = types.SyncStates.READY, _on6) }, _states) }, { actions: { onReady: /*#__PURE__*/xstate.assign(function (_, event) { return { valid: true, ready: true, shopName: event.shopName, errorMessage: undefined, error: undefined }; }), onSetup: /*#__PURE__*/xstate.assign({ valid: false, ready: true }), onError: /*#__PURE__*/xstate.assign({ errorMessage: function errorMessage(_, event) { return event.errorMessage; }, error: function error(_, event) { return event.error; }, valid: false }), onDocumentsFetched: /*#__PURE__*/xstate.assign({ documentsFetched: function documentsFetched(context, action) { return [].concat(context.documentsFetched, action.shopifyDocuments); } }), onFetchedComplete: /*#__PURE__*/xstate.assign({ toSync: function toSync(context) { return context.documentsFetched; }, toLink: function toLink(context) { return context.documentsFetched; } }), onDocumentsSynced: /*#__PURE__*/xstate.assign({ syncOperations: function syncOperations(context, action) { return [].concat(context.syncOperations, [action.op]); } }), onDocumentLinked: /*#__PURE__*/xstate.assign({ linkOperations: function linkOperations(context, action) { return [].concat(context.linkOperations, [action.op]); } }) } }); var syncStateMachine = function syncStateMachine(_ref) { var onStateChange = _ref.onStateChange; var initialState = syncMachine.initialState; var service = xstate.interpret(syncMachine); service.start(); service.onTransition(function (newState) { onStateChange(newState); }); var init = function init(valid, shopName) { if (valid) { service.send({ type: types.SyncEventType.Valid, shopName: shopName }); } else { service.send(types.SyncEventType.Invalid); } }; var onSavedSecrets = function onSavedSecrets(shopName) { service.send({ type: types.SyncEventType.Valid, shopName: shopName }); }; var onSavedSecretsError = function onSavedSecretsError(error, message) { var errorMessage = message || error.message; service.send({ type: types.SyncEventType.Errored, error: error, errorMessage: errorMessage }); }; var onClearedSecrets = function onClearedSecrets() { service.send({ type: types.SyncEventType.ClearedSecrets }); }; var startSync = function startSync() { service.send(types.SyncEventType.Sync); }; var onDocumentsFetched = function onDocumentsFetched(shopifyDocuments) { service.send({ type: types.SyncEventType.DocumentsFetched, shopifyDocuments: shopifyDocuments }); }; var onFetchComplete = function onFetchComplete(shopifyDocuments) { service.send({ type: types.SyncEventType.FetchComplete, shopifyDocuments: shopifyDocuments }); }; var onDocumentSynced = function onDocumentSynced(op) { service.send({ type: types.SyncEventType.DocumentsSynced, op: op }); }; var onDocumentLinked = function onDocumentLinked(op) { service.send({ type: types.SyncEventType.DocumentsLinked, op: op }); }; var onComplete = function onComplete() { service.send({ type: types.SyncEventType.Complete }); }; var onError = function onError(error) { service.send({ type: types.SyncEventType.Errored, errorMessage: error.message, error: error }); }; return { initialState: initialState, startSync: startSync, init: init, onSavedSecrets: onSavedSecrets, onSavedSecretsError: onSavedSecretsError, onClearedSecrets: onClearedSecrets, onDocumentsFetched: onDocumentsFetched, onFetchComplete: onFetchComplete, onDocumentSynced: onDocumentSynced, onDocumentLinked: onDocumentLinked, onComplete: onComplete, onError: onError }; }; var log = /*#__PURE__*/Debug('sane-shopify:patch'); var createLogger = function createLogger(cbs) { if (cbs === void 0) { cbs = {}; } var logFetched = function logFetched(fetchedItems) { var shopifyDocuments = Array.isArray(fetchedItems) ? fetchedItems : [fetchedItems]; log('fetched initial shopify documents:', shopifyDocuments); cbs.onProgress && cbs.onProgress({ type: 'fetched', shopifyDocuments: shopifyDocuments }); }; var logSynced = function logSynced(op) { log('synced document', op); cbs.onProgress && cbs.onProgress(op); }; var logLinked = function logLinked(sourceDoc, pairs) { log('linked documents:', pairs); cbs.onProgress && cbs.onProgress({ type: 'link', sourceDoc: sourceDoc, pairs: pairs }); }; var logArchived = function logArchived(sourceDoc) { log('archived document:', sourceDoc); cbs.onProgress && cbs.onProgress({ type: 'archive', sourceDoc: sourceDoc }); }; var logComplete = function logComplete(op) { log('completed sync operations', op); var ops = Array.isArray(op) ? op : [op]; cbs.onComplete && cbs.onComplete(ops, 'completed sync operations'); }; return { logFetched: logFetched, logLinked: logLinked, logSynced: logSynced, logComplete: logComplete, logArchived: logArchived }; }; var CONFIG_DOC_TYPE = 'sane-shopify.storefront-config'; var CONFIG_DOC_ID_PREFIX = 'sane-shopify-storefront-config-'; var STOREFRONT_API_VERSION = '2022-10'; var getErrorMessage = function getErrorMessage(r) { switch (r.status) { case 401: case 403: return 'Authentication failed. Please make sure you have entered the correct Storefront name and API Key.'; default: return "There was an error connecting to Shopify (" + r.status + ": " + r.statusText + ")"; } }; var deduplicateFragments = function deduplicateFragments(queryString) { if (!queryString) throw new Error('No query string provided'); return queryString.split(/\n\s+\n/).map(function (group) { return group.replace(/^([\n\s])+/, '').replace(/\n+$/, ''); }).reduce(function (acc, current) { if (acc.includes(current)) return acc; return [].concat(acc, [current]); }, []).join('\n\n'); }; var createShopifyClient = function createShopifyClient(secrets) { if (!secrets) { return { query: function () { var _query = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() { return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: throw new Error('You must provide a shopify storefront name and access token'); case 1: case "end": return _context.stop(); } }, _callee); })); function query() { return _query.apply(this, arguments); } return query; }(), shopName: '' }; } var shopName = secrets.shopName, accessToken = secrets.accessToken; var url = "https://" + shopName + ".myshopify.com/api/" + STOREFRONT_API_VERSION + "/graphql.json"; var headers = { 'Content-Type': 'application/json', 'X-Shopify-Storefront-Access-Token': accessToken }; var bucket = new LeakyBucket({ capacity: 40, interval: 30 }); var query = /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(q, variables) { var _q$loc; var queryString; return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: queryString = typeof q === 'string' ? q : deduplicateFragments(q == null ? void 0 : (_q$loc = q.loc) == null ? void 0 : _q$loc.source.body); _context3.next = 3; return bucket.throttle(); case 3: return _context3.abrupt("return", fetch(url, { headers: headers, method: 'POST', body: JSON.stringify({ variables: variables, query: queryString }) }).then( /*#__PURE__*/function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(r) { var json; return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: if (r.ok) { _context2.next = 2; break; } throw new Error(getErrorMessage(r)); case 2: _context2.next = 4; return r.json(); case 4: json = _context2.sent; return _context2.abrupt("return", json); case 6: case "end": return _context2.stop(); } }, _callee2); })); return function (_x3) { return _ref2.apply(this, arguments); }; }())); case 4: case "end": return _context3.stop(); } }, _callee3); })); return function query(_x, _x2) { return _ref.apply(this, arguments); }; }(); return { shopName: shopName, query: query }; }; var isMetafield = function isMetafield(obj) { return Boolean(obj && obj.value && obj.namespace && obj.key); }; var omit = function omit(obj, toOmit) { return Object.entries(obj).reduce(function (prev, _ref) { var _extends2; var key = _ref[0], value = _ref[1]; if (key === toOmit) return prev; return _extends({}, prev, (_extends2 = {}, _extends2[key] = value, _extends2)); }, {}); }; var remapMetafields = function remapMetafields(item) { var _remapped$variants, _remapped$variants$ed; var itemWithEmptyMetafields = _extends({}, item, { metafields: { pageInfo: { hasNextPage: false, hasPreviousPage: false }, edges: [] } }); var remapped = Object.entries(item).reduce(function (prev, _ref2) { var key = _ref2[0], value = _ref2[1]; var metafieldMatch = key.match(/metafield_/); if (metafieldMatch != null && metafieldMatch.input && value === null) { return omit(prev, key); } if (metafieldMatch != null && metafieldMatch.input && isMetafield(value)) { var _prev$metafields; var metafieldMatchName = metafieldMatch.input; var prevWithoutMetafieldProperty = omit(prev, metafieldMatchName); var prevEdges = (prev == null ? void 0 : (_prev$metafields = prev.metafields) == null ? void 0 : _prev$metafields.edges) || []; var metafieldValue = value.value, namespace = value.namespace, _key = value.key; var newEdge = { _key: namespace + "-" + _key + "-" + metafieldValue, cursor: namespace + "-" + _key + "-" + metafieldValue, node: { namespace: namespace, key: _key, value: metafieldValue } }; return _extends({}, prevWithoutMetafieldProperty, { metafields: _extends({}, prev.metafields, { edges: [].concat(prevEdges, [newEdge]) }) }); } return prev; }, itemWithEmptyMetafields); if (remapped.__typename === 'Product' && 'variants' in remapped && remapped != null && (_remapped$variants = remapped.variants) != null && (_remapped$variants$ed = _remapped$variants.edges) != null && _remapped$variants$ed.length) { return _extends({}, remapped, { variants: _extends({}, remapped.variants, { edges: remapped.variants.edges.map(function (variantEdge) { return _extends({}, variantEdge, { node: remapMetafields(variantEdge.node) }); }) }) }); } return remapped; }; var isSanityProduct = function isSanityProduct(doc) { return doc._type === 'shopifyProduct'; }; var isSanityCollection = function isSanityCollection(doc) { return doc._type === 'shopifyCollection'; }; var isShopifyProduct = function isShopifyProduct(doc) { return doc.__typename === 'Product'; }; var isShopifyCollection = function isShopifyCollection(doc) { return doc.__typename === 'Collection'; }; function definitely(items) { if (!items) return []; return items.filter(function (i) { return Boolean(i); }); } var slugify = function slugify(text) { return text.toString().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]+/g, '').replace(/\-\-+/g, '-').replace(/^-+/, '').replace(/-+$/, ''); }; var last = function last(arr) { return arr[arr.length - 1]; }; var getLastCursor = function getLastCursor(connection) { if (!connection.edges || connection.edges.length === 0) return null; var lastEdge = last(connection == null ? void 0 : connection.edges); if (!lastEdge) return null; return lastEdge.cursor; }; var mergePaginatedResults = function mergePaginatedResults(p1, p2) { if (!p1.pageInfo || !p2.pageInfo) throw new Error('Page info was not suplied'); return { pageInfo: { hasPrevPage: p1.pageInfo.hasPreviousPage, hasPreviousPage: p1.pageInfo.hasPreviousPage, hasNextPage: p2.pageInfo.hasNextPage }, edges: [].concat(definitely(p1.edges), definitely(p2.edges)) }; }; var toStorefrontId = function toStorefrontId(id) { return id.startsWith('gid://') ? btoa(id) : id; }; var toAdminApiId = function toAdminApiId(id) { return id.startsWith('gid://') ? id : atob(id); }; var _templateObject, _templateObject2, _templateObject3; var moneyFragment = "\n fragment MoneyV2Fragment on MoneyV2 {\n amount\n currencyCode\n }\n"; var mediaImageFragment = "\n fragment MediaImageFragment on MediaImage {\n image {\n __typename\n id\n altText\n originalSrc\n w100: transformedSrc(maxWidth: 100, crop: CENTER)\n w300: transformedSrc(maxWidth: 300, crop: CENTER)\n w800: transformedSrc(maxWidth: 800, crop: CENTER)\n w1200: transformedSrc(maxWidth: 1200, crop: CENTER)\n w1600: transformedSrc(maxWidth: 1600, crop: CENTER)\n }\n }\n"; var videoFragment = "\n fragment VideoFragment on Video {\n id\n alt\n sources {\n url\n format\n mimeType\n }\n }\n"; var imageFragment = "\n fragment ImageFragment on Image {\n __typename\n id\n altText\n originalSrc\n w100: transformedSrc(maxWidth: 100, crop: CENTER)\n w300: transformedSrc(maxWidth: 300, crop: CENTER)\n w800: transformedSrc(maxWidth: 800, crop: CENTER)\n w1200: transformedSrc(maxWidth: 1200, crop: CENTER)\n w1600: transformedSrc(maxWidth: 1600, crop: CENTER)\n }\n"; var createProductVariantFragment = function createProductVariantFragment(shopifyConfig) { var _shopifyConfig$varian; return gql(_templateObject || (_templateObject = _taggedTemplateLiteralLoose(["\n fragment ProductVariantFragment on ProductVariant {\n __typename\n availableForSale\n currentlyNotInStock\n id\n image {\n ...ImageFragment\n }\n priceV2 {\n ...MoneyV2Fragment\n }\n compareAtPriceV2 {\n ...MoneyV2Fragment\n }\n selectedOptions {\n value\n name\n }\n ", "\n requiresShipping\n sku\n title\n weight\n weightUnit\n }\n"])), metafieldsToQuery(shopifyConfig == null ? void 0 : (_shopifyConfig$varian = shopifyConfig.variants) == null ? void 0 : _shopifyConfig$varian.metafields)); }; var metafieldsToQuery = function metafieldsToQuery(metafields) { return metafields ? metafields.map(function (_ref) { var namespace = _ref.namespace, key = _ref.key; return "\n metafield_" + namespace + "_" + key + ": metafield(namespace: \"" + namespace + "\", key: \"" + key + "\"){\n namespace\n key\n value\n }\n "; }) : ''; }; var createProductFragment = function createProductFragment(shopifyConfig) { var _shopifyConfig$produc; return gql(_templateObject2 || (_templateObject2 = _taggedTemplateLiteralLoose(["\n fragment ProductFragment on Product {\n __typename\n id\n updatedAt\n handle\n title\n description\n descriptionHtml\n availableForSale\n productType\n tags\n vendor\n createdAt\n publishedAt\n options {\n id\n name\n values\n }\n variants(first: 99) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n ...ProductVariantFragment\n }\n }\n }\n ", "\n compareAtPriceRange {\n minVariantPrice {\n ...MoneyV2Fragment\n }\n maxVariantPrice {\n ...MoneyV2Fragment\n }\n }\n priceRange {\n minVariantPrice {\n ...MoneyV2Fragment\n }\n maxVariantPrice {\n ...MoneyV2Fragment\n }\n }\n media(first: 50) {\n edges {\n cursor\n node {\n ...MediaImageFragment\n ...VideoFragment\n }\n }\n }\n images(first: 50) {\n edges {\n cursor\n node {\n ...ImageFragment\n }\n }\n }\n }\n ", "\n ", "\n ", "\n ", "\n ", "\n"])), metafieldsToQuery(shopifyConfig == null ? void 0 : (_shopifyConfig$produc = shopifyConfig.products) == null ? void 0 : _shopifyConfig$produc.metafields), moneyFragment, mediaImageFragment, imageFragment, videoFragment, createProductVariantFragment(shopifyConfig)); }; var createCollectionFragment = function createCollectionFragment(shopifyConfig) { var _shopifyConfig$collec; return gql(_templateObject3 || (_templateObject3 = _taggedTemplateLiteralLoose(["\n fragment CollectionFragment on Collection {\n __typename\n id\n updatedAt\n handle\n title\n description\n descriptionHtml\n ", "\n image {\n ...ImageFragment\n }\n }\n ", "\n"])), metafieldsToQuery(shopifyConfig == null ? void 0 : (_shopifyConfig$collec = shopifyConfig.collections) == null ? void 0 : _shopifyConfig$collec.metafields), imageFragment); }; var _templateObject$1, _templateObject2$1; var log$1 = /*#__PURE__*/Debug('sane-shopify:fetching'); var createProductByHandle = function createProductByHandle(shopifyConfig) { return gql(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteralLoose(["\n query ProductQuery(\n $handle: String!\n $collectionsFirst: Int!\n $collectionsAfter: String\n ) {\n productByHandle(handle: $handle) {\n ...ProductFragment\n collections(first: $collectionsFirst, after: $collectionsAfter) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n id\n handle\n }\n }\n }\n }\n }\n ", "\n"])), createProductFragment(shopifyConfig)); }; var createProductById = function createProductById(shopifyConfig) { return gql(_templateObject2$1 || (_templateObject2$1 = _taggedTemplateLiteralLoose(["\n query NodeQuery(\n $id: ID!\n $collectionsFirst: Int!\n $collectionsAfter: String\n ) {\n node(id: $id) {\n ... on Product {\n ...ProductFragment\n collections(first: $collectionsFirst, after: $collectionsAfter) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n id\n handle\n }\n }\n }\n }\n }\n }\n ", "\n"])), createProductFragment(shopifyConfig)); }; var createQueries = function createQueries(shopifyConfig) { return { PRODUCT_BY_ID: createProductById(shopifyConfig), PRODUCT_BY_HANDLE: createProductByHandle(shopifyConfig) }; }; var getByHandle = /*#__PURE__*/function () { var _ref = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(shopifyClient, queryString, handle, collectionsAfter) { var _result$data; var result, product; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.next = 2; return shopifyClient.query(queryString, { handle: handle, collectionsFirst: 200, collectionsAfter: collectionsAfter }); case 2: result = _context.sent; product = result == null ? void 0 : (_result$data = result.data) == null ? void 0 : _result$data.productByHandle; return _context.abrupt("return", product ? remapMetafields(product) : undefined); case 5: case "end": return _context.stop(); } }, _callee); })); return function getByHandle(_x, _x2, _x3, _x4) { return _ref.apply(this, arguments); }; }(); var getById = /*#__PURE__*/function () { var _ref2 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(shopifyClient, queryString, id, collectionsAfter) { var _result$data2; var result, product; return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return shopifyClient.query(queryString, { id: id, collectionsFirst: 20, collectionsAfter: collectionsAfter }); case 2: result = _context2.sent; product = result == null ? void 0 : (_result$data2 = result.data) == null ? void 0 : _result$data2.node; return _context2.abrupt("return", product ? remapMetafields(product) : undefined); case 5: case "end": return _context2.stop(); } }, _callee2); })); return function getById(_x5, _x6, _x7, _x8) { return _ref2.apply(this, arguments); }; }(); var fetchAllProductCollections = /*#__PURE__*/function () { var _ref3 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(shopifyClient, metafieldsConfig, prevProduct) { var _prevProduct$collecti, _prevProduct$collecti2, _product$collections, _product$collections$; var collectionsAfter, _createQueries, PRODUCT_BY_HANDLE, nextProduct, product; return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: if ((_prevProduct$collecti = prevProduct.collections) != null && (_prevProduct$collecti2 = _prevProduct$collecti.pageInfo) != null && _prevProduct$collecti2.hasNextPage) { _context3.next = 3; break; } log$1("Fetched all collections for product " + prevProduct.handle, prevProduct); return _context3.abrupt("return", prevProduct); case 3: collectionsAfter = getLastCursor(prevProduct.collections); log$1("Fetching further products for product " + prevProduct.handle, prevProduct); _createQueries = createQueries(metafieldsConfig), PRODUCT_BY_HANDLE = _createQueries.PRODUCT_BY_HANDLE; _context3.next = 8; return getByHandle(shopifyClient, PRODUCT_BY_HANDLE, prevProduct.handle, collectionsAfter ? collectionsAfter : undefined); case 8: nextProduct = _context3.sent; product = nextProduct ? _extends({}, nextProduct, { collections: nextProduct.collections ? mergePaginatedResults(prevProduct.collections, nextProduct.collections) : prevProduct.collections }) : prevProduct; if (!(product != null && (_product$collections = product.collections) != null && (_product$collections$ = _product$collections.pageInfo) != null && _product$collections$.hasNextPage)) { _context3.next = 12; break; } return _context3.abrupt("return", fetchAllProductCollections(shopifyClient, metafieldsConfig, product)); case 12: log$1("Fetched all collections for product " + prevProduct.handle, prevProduct); return _context3.abrupt("return", product); case 14: case "end": return _context3.stop(); } }, _callee3); })); return function fetchAllProductCollections(_x9, _x10, _x11) { return _ref3.apply(this, arguments); }; }(); var createFetchShopifyProduct = function createFetchShopifyProduct(shopifyClient, fetchMetafieldsConfig, cache) { return /*#__PURE__*/function () { var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(params) { var metafieldsConfig, id, handle, _createQueries2, PRODUCT_BY_ID, PRODUCT_BY_HANDLE, cachedProduct, fetchedProduct, product; return _regeneratorRuntime().wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: _context4.next = 2; return fetchMetafieldsConfig(); case 2: metafieldsConfig = _context4.sent; id = params.id, handle = params.handle; if (!(!id && !handle)) { _context4.next = 6; break; } throw new Error('You must provide either an id or handle'); case 6: _createQueries2 = createQueries(metafieldsConfig), PRODUCT_BY_ID = _createQueries2.PRODUCT_BY_ID, PRODUCT_BY_HANDLE = _createQueries2.PRODUCT_BY_HANDLE; cachedProduct = id ? cache.getProductById(id) : handle ? cache.getProductByHandle(handle) : null; if (!cachedProduct) { _context4.next = 10; break; } return _context4.abrupt("return", fetchAllProductCollections(shopifyClient, metafieldsConfig, cachedProduct)); case 10: if (!id) { _context4.next = 16; break; } _context4.next = 13; return getById(shopifyClient, PRODUCT_BY_ID, id); case 13: _context4.t0 = _context4.sent; _context4.next = 24; break; case 16: if (!handle) { _context4.next = 22; break; } _context4.next = 19; return getByHandle(shopifyClient, PRODUCT_BY_HANDLE, handle); case 19: _context4.t1 = _context4.sent; _context4.next = 23; break; case 22: _context4.t1 = null; case 23: _context4.t0 = _context4.t1; case 24: fetchedProduct = _context4.t0; if (fetchedProduct) { _context4.next = 27; break; } return _context4.abrupt("return", null); case 27: _context4.next = 29; return fetchAllProductCollections(shopifyClient, metafieldsConfig, fetchedProduct); case 29: product = _context4.sent; cache.set(product); return _context4.abrupt("return", product); case 32: case "end": return _context4.stop(); } }, _callee4); })); return function (_x12) { return _ref4.apply(this, arguments); }; }(); }; var _templateObject$2, _templateObject2$2; var log$2 = /*#__PURE__*/Debug('sane-shopify:fetching'); var createCollectionByHandle = function createCollectionByHandle(shopifyConfig) { return gql(_templateObject$2 || (_templateObject$2 = _taggedTemplateLiteralLoose(["\n query CollectionQuery(\n $handle: String!\n $productsFirst: Int!\n $productsAfter: String\n ) {\n collectionByHandle(handle: $handle) {\n ...CollectionFragment\n products(first: $productsFirst, after: $productsAfter) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n id\n handle\n }\n }\n }\n }\n }\n ", "\n"])), createCollectionFragment(shopifyConfig)); }; var createCollectionById = function createCollectionById(shopifyConfig) { return gql(_templateObject2$2 || (_templateObject2$2 = _taggedTemplateLiteralLoose(["\n query NodeQuery($id: ID!, $productsFirst: Int!, $productsAfter: String) {\n node(id: $id) {\n ... on Collection {\n ...CollectionFragment\n products(first: $productsFirst, after: $productsAfter) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n id\n handle\n }\n }\n }\n }\n }\n }\n ", "\n"])), createCollectionFragment(shopifyConfig)); }; var createQueries$1 = function createQueries(shopifyConfig) { return { COLLECTION_BY_ID: createCollectionById(shopifyConfig), COLLECTION_BY_HANDLE: createCollectionByHandle(shopifyConfig) }; }; var getByHandle$1 = /*#__PURE__*/function () { var _ref = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(shopifyClient, queryString, handle, productsAfter) { var _result$data; var result; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.next = 2; return shopifyClient.query(queryString, { handle: handle, productsFirst: 50, productsAfter: productsAfter }); case 2: result = _context.sent; return _context.abrupt("return", result == null ? void 0 : (_result$data = result.data) == null ? void 0 : _result$data.collectionByHandle); case 4: case "end": return _context.stop(); } }, _callee); })); return function getByHandle(_x, _x2, _x3, _x4) { return _ref.apply(this, arguments); }; }(); var getById$1 = /*#__PURE__*/function () { var _ref2 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(shopifyClient, queryString, id, productsAfter) { var _result$data2; var result; return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return shopifyClient.query(queryString, { id: id, productsFirst: 200, productsAfter: productsAfter }); case 2: result = _context2.sent; return _context2.abrupt("return", result == null ? void 0 : (_result$data2 = result.data) == null ? void 0 : _result$data2.node); case 4: case "end": return _context2.stop(); } }, _callee2); })); return function getById(_x5, _x6, _x7, _x8) { return _ref2.apply(this, arguments); }; }(); var fetchAllCollectionProducts = /*#__PURE__*/function () { var _ref3 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(shopifyClient, metafieldsConfig, prevCollection) { var _prevCollection$produ, _prevCollection$produ2, _collection$products, _collection$products$; var productsAfter, _createQueries, COLLECTION_BY_HANDLE, nextCollection, collection; return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: if ((_prevCollection$produ = prevCollection.products) != null && (_prevCollection$produ2 = _prevCollection$produ.pageInfo) != null && _prevCollection$produ2.hasNextPage) { _context3.next = 3; brea