UNPKG

prismic-javascript

Version:
1,243 lines (1,226 loc) 51.6 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('cross-fetch')) : typeof define === 'function' && define.amd ? define(['cross-fetch'], factory) : (global = global || self, global.PrismicJS = factory(global.fetch)); }(this, (function (crossFetch) { 'use strict'; crossFetch = crossFetch && Object.prototype.hasOwnProperty.call(crossFetch, 'default') ? crossFetch['default'] : crossFetch; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var Variation = /** @class */ (function () { function Variation(data) { this.data = {}; this.data = data; } Variation.prototype.id = function () { return this.data.id; }; Variation.prototype.ref = function () { return this.data.ref; }; Variation.prototype.label = function () { return this.data.label; }; return Variation; }()); var Experiment = /** @class */ (function () { function Experiment(data) { this.data = {}; this.data = data; this.variations = (data.variations || []).map(function (v) { return new Variation(v); }); } Experiment.prototype.id = function () { return this.data.id; }; Experiment.prototype.googleId = function () { return this.data.googleId; }; Experiment.prototype.name = function () { return this.data.name; }; return Experiment; }()); var Experiments = /** @class */ (function () { function Experiments(data) { if (data) { this.drafts = (data.drafts || []).map(function (exp) { return new Experiment(exp); }); this.running = (data.running || []).map(function (exp) { return new Experiment(exp); }); } } Experiments.prototype.current = function () { if (this.running.length > 0) { return this.running[0]; } else { return null; } }; Experiments.prototype.refFromCookie = function (cookie) { if (!cookie || cookie.trim() === '') return null; var splitted = cookie.trim().split(' '); if (splitted.length < 2) return null; var expId = splitted[0]; var varIndex = parseInt(splitted[1], 10); var exp = this.running.filter(function (exp) { return exp.googleId() === expId && exp.variations.length > varIndex; })[0]; return exp ? exp.variations[varIndex].ref() : null; }; return Experiments; }()); var LazySearchForm = /** @class */ (function () { function LazySearchForm(id, api) { this.id = id; this.api = api; this.fields = {}; } LazySearchForm.prototype.set = function (key, value) { this.fields[key] = value; return this; }; LazySearchForm.prototype.ref = function (ref) { return this.set('ref', ref); }; LazySearchForm.prototype.query = function (query) { return this.set('q', query); }; LazySearchForm.prototype.pageSize = function (size) { return this.set('pageSize', size); }; LazySearchForm.prototype.fetch = function (fields) { console.warn('Warning: Using Fetch is deprecated. Use the property `graphQuery` instead.'); return this.set('fetch', fields); }; LazySearchForm.prototype.fetchLinks = function (fields) { console.warn('Warning: Using FetchLinks is deprecated. Use the property `graphQuery` instead.'); return this.set('fetchLinks', fields); }; LazySearchForm.prototype.graphQuery = function (query) { return this.set('graphQuery', query); }; LazySearchForm.prototype.lang = function (langCode) { return this.set('lang', langCode); }; LazySearchForm.prototype.page = function (p) { return this.set('page', p); }; LazySearchForm.prototype.after = function (documentId) { return this.set('after', documentId); }; LazySearchForm.prototype.orderings = function (orderings) { return this.set('orderings', orderings); }; LazySearchForm.prototype.url = function () { var _this = this; return this.api.get().then(function (api) { return LazySearchForm.toSearchForm(_this, api).url(); }); }; LazySearchForm.prototype.submit = function (cb) { var _this = this; return this.api.get().then(function (api) { return LazySearchForm.toSearchForm(_this, api).submit(cb); }); }; LazySearchForm.toSearchForm = function (lazyForm, api) { var form = api.form(lazyForm.id); if (form) { return Object.keys(lazyForm.fields).reduce(function (form, fieldKey) { var fieldValue = lazyForm.fields[fieldKey]; if (fieldKey === 'q') { return form.query(fieldValue); } else if (fieldKey === 'pageSize') { return form.pageSize(fieldValue); } else if (fieldKey === 'fetch') { return form.fetch(fieldValue); } else if (fieldKey === 'fetchLinks') { return form.fetchLinks(fieldValue); } else if (fieldKey === 'graphQuery') { return form.graphQuery(fieldValue); } else if (fieldKey === 'lang') { return form.lang(fieldValue); } else if (fieldKey === 'page') { return form.page(fieldValue); } else if (fieldKey === 'after') { return form.after(fieldValue); } else if (fieldKey === 'orderings') { return form.orderings(fieldValue); } else { return form.set(fieldKey, fieldValue); } }, form); } else { throw new Error("Unable to access to form " + lazyForm.id); } }; return LazySearchForm; }()); var SearchForm = /** @class */ (function () { function SearchForm(form, httpClient) { this.httpClient = httpClient; this.form = form; this.data = {}; for (var field in form.fields) { if (form.fields[field]['default']) { this.data[field] = [form.fields[field]['default']]; } } } SearchForm.prototype.set = function (field, value) { var fieldDesc = this.form.fields[field]; if (!fieldDesc) throw new Error('Unknown field ' + field); var checkedValue = value === '' || value === undefined ? null : value; var values = this.data[field] || []; if (fieldDesc.multiple) { values = checkedValue ? values.concat([checkedValue]) : values; } else { values = checkedValue ? [checkedValue] : values; } this.data[field] = values; return this; }; /** * Sets a ref to query on for this SearchForm. This is a mandatory * method to call before calling submit(), and api.form('everything').submit() * will not work. */ SearchForm.prototype.ref = function (ref) { return this.set('ref', ref); }; /** * Sets a predicate-based query for this SearchForm. This is where you * paste what you compose in your prismic.io API browser. */ SearchForm.prototype.query = function (query) { if (typeof query === 'string') { return this.query([query]); } else if (Array.isArray(query)) { return this.set('q', "[" + query.join('') + "]"); } else { throw new Error("Invalid query : " + query); } }; /** * Sets a page size to query for this SearchForm. This is an optional method. * * @param {number} size - The page size * @returns {SearchForm} - The SearchForm itself */ SearchForm.prototype.pageSize = function (size) { return this.set('pageSize', size); }; /** * Restrict the results document to the specified fields */ SearchForm.prototype.fetch = function (fields) { console.warn('Warning: Using Fetch is deprecated. Use the property `graphQuery` instead.'); var strFields = Array.isArray(fields) ? fields.join(',') : fields; return this.set('fetch', strFields); }; /** * Include the requested fields in the DocumentLink instances in the result */ SearchForm.prototype.fetchLinks = function (fields) { console.warn('Warning: Using FetchLinks is deprecated. Use the property `graphQuery` instead.'); var strFields = Array.isArray(fields) ? fields.join(',') : fields; return this.set('fetchLinks', strFields); }; /** * Sets the graphquery to query for this SearchForm. This is an optional method. */ SearchForm.prototype.graphQuery = function (query) { return this.set('graphQuery', query); }; /** * Sets the language to query for this SearchForm. This is an optional method. */ SearchForm.prototype.lang = function (langCode) { return this.set('lang', langCode); }; /** * Sets the page number to query for this SearchForm. This is an optional method. */ SearchForm.prototype.page = function (p) { return this.set('page', p); }; /** * Remove all the documents except for those after the specified document in the list. This is an optional method. */ SearchForm.prototype.after = function (documentId) { return this.set('after', documentId); }; /** * Sets the orderings to query for this SearchForm. This is an optional method. */ SearchForm.prototype.orderings = function (orderings) { if (!orderings) { return this; } else { return this.set('orderings', "[" + orderings.join(',') + "]"); } }; /** * Build the URL to query */ SearchForm.prototype.url = function () { var url = this.form.action; if (this.data) { var sep = (url.indexOf('?') > -1 ? '&' : '?'); for (var key in this.data) { if (Object.prototype.hasOwnProperty.call(this.data, key)) { var values = this.data[key]; if (values) { for (var i = 0; i < values.length; i++) { url += sep + key + '=' + encodeURIComponent(values[i]); sep = '&'; } } } } } return url; }; /** * Submits the query, and calls the callback function. */ SearchForm.prototype.submit = function (cb) { return this.httpClient.cachedRequest(this.url()).then(function (response) { cb && cb(null, response); return response; }).catch(function (error) { cb && cb(error); throw error; }); }; return SearchForm; }()); var OPERATOR = { at: 'at', not: 'not', missing: 'missing', has: 'has', any: 'any', in: 'in', fulltext: 'fulltext', similar: 'similar', numberGt: 'number.gt', numberLt: 'number.lt', numberInRange: 'number.inRange', dateBefore: 'date.before', dateAfter: 'date.after', dateBetween: 'date.between', dateDayOfMonth: 'date.day-of-month', dateDayOfMonthAfter: 'date.day-of-month-after', dateDayOfMonthBefore: 'date.day-of-month-before', dateDayOfWeek: 'date.day-of-week', dateDayOfWeekAfter: 'date.day-of-week-after', dateDayOfWeekBefore: 'date.day-of-week-before', dateMonth: 'date.month', dateMonthBefore: 'date.month-before', dateMonthAfter: 'date.month-after', dateYear: 'date.year', dateHour: 'date.hour', dateHourBefore: 'date.hour-before', dateHourAfter: 'date.hour-after', GeopointNear: 'geopoint.near', }; function encode(value) { if (typeof value === 'string') { return "\"" + value + "\""; } else if (typeof value === 'number') { return value.toString(); } else if (value instanceof Date) { return value.getTime().toString(); } else if (Array.isArray(value)) { return "[" + value.map(function (v) { return encode(v); }).join(',') + "]"; } else if (typeof value === "boolean") { return value.toString(); } else { throw new Error("Unable to encode " + value + " of type " + typeof value); } } var geopoint = { near: function (fragment, latitude, longitude, radius) { return "[" + OPERATOR.GeopointNear + "(" + fragment + ", " + latitude + ", " + longitude + ", " + radius + ")]"; }, }; var date = { before: function (fragment, before) { return "[" + OPERATOR.dateBefore + "(" + fragment + ", " + encode(before) + ")]"; }, after: function (fragment, after) { return "[" + OPERATOR.dateAfter + "(" + fragment + ", " + encode(after) + ")]"; }, between: function (fragment, before, after) { return "[" + OPERATOR.dateBetween + "(" + fragment + ", " + encode(before) + ", " + encode(after) + ")]"; }, dayOfMonth: function (fragment, day) { return "[" + OPERATOR.dateDayOfMonth + "(" + fragment + ", " + day + ")]"; }, dayOfMonthAfter: function (fragment, day) { return "[" + OPERATOR.dateDayOfMonthAfter + "(" + fragment + ", " + day + ")]"; }, dayOfMonthBefore: function (fragment, day) { return "[" + OPERATOR.dateDayOfMonthBefore + "(" + fragment + ", " + day + ")]"; }, dayOfWeek: function (fragment, day) { return "[" + OPERATOR.dateDayOfWeek + "(" + fragment + ", " + encode(day) + ")]"; }, dayOfWeekAfter: function (fragment, day) { return "[" + OPERATOR.dateDayOfWeekAfter + "(" + fragment + ", " + encode(day) + ")]"; }, dayOfWeekBefore: function (fragment, day) { return "[" + OPERATOR.dateDayOfWeekBefore + "(" + fragment + ", " + encode(day) + ")]"; }, month: function (fragment, month) { return "[" + OPERATOR.dateMonth + "(" + fragment + ", " + encode(month) + ")]"; }, monthBefore: function (fragment, month) { return "[" + OPERATOR.dateMonthBefore + "(" + fragment + ", " + encode(month) + ")]"; }, monthAfter: function (fragment, month) { return "[" + OPERATOR.dateMonthAfter + "(" + fragment + ", " + encode(month) + ")]"; }, year: function (fragment, year) { return "[" + OPERATOR.dateYear + "(" + fragment + ", " + year + ")]"; }, hour: function (fragment, hour) { return "[" + OPERATOR.dateHour + "(" + fragment + ", " + hour + ")]"; }, hourBefore: function (fragment, hour) { return "[" + OPERATOR.dateHourBefore + "(" + fragment + ", " + hour + ")]"; }, hourAfter: function (fragment, hour) { return "[" + OPERATOR.dateHourAfter + "(" + fragment + ", " + hour + ")]"; }, }; var number = { gt: function (fragment, value) { return "[" + OPERATOR.numberGt + "(" + fragment + ", " + value + ")]"; }, lt: function (fragment, value) { return "[" + OPERATOR.numberLt + "(" + fragment + ", " + value + ")]"; }, inRange: function (fragment, before, after) { return "[" + OPERATOR.numberInRange + "(" + fragment + ", " + before + ", " + after + ")]"; }, }; var Predicates = { at: function (fragment, value) { return "[" + OPERATOR.at + "(" + fragment + ", " + encode(value) + ")]"; }, not: function (fragment, value) { return "[" + OPERATOR.not + "(" + fragment + ", " + encode(value) + ")]"; }, missing: function (fragment) { return "[" + OPERATOR.missing + "(" + fragment + ")]"; }, has: function (fragment) { return "[" + OPERATOR.has + "(" + fragment + ")]"; }, any: function (fragment, values) { return "[" + OPERATOR.any + "(" + fragment + ", " + encode(values) + ")]"; }, in: function (fragment, values) { return "[" + OPERATOR.in + "(" + fragment + ", " + encode(values) + ")]"; }, fulltext: function (fragment, value) { return "[" + OPERATOR.fulltext + "(" + fragment + ", " + encode(value) + ")]"; }, similar: function (documentId, maxResults) { return "[" + OPERATOR.similar + "(\"" + documentId + "\", " + maxResults + ")]"; }, date: date, dateBefore: date.before, dateAfter: date.after, dateBetween: date.between, dayOfMonth: date.dayOfMonth, dayOfMonthAfter: date.dayOfMonthAfter, dayOfMonthBefore: date.dayOfMonthBefore, dayOfWeek: date.dayOfWeek, dayOfWeekAfter: date.dayOfWeekAfter, dayOfWeekBefore: date.dayOfWeekBefore, month: date.month, monthBefore: date.monthBefore, monthAfter: date.monthAfter, year: date.year, hour: date.hour, hourBefore: date.hourBefore, hourAfter: date.hourAfter, number: number, gt: number.gt, lt: number.lt, inRange: number.inRange, near: geopoint.near, geopoint: geopoint, }; /* eslint-disable */ // Some portions of code from https://github.com/jshttp/cookie var decode = decodeURIComponent; function tryDecode(str, decode) { try { return decode(str); } catch (e) { return str; } } function parse(str, options) { if (typeof str !== 'string') { throw new TypeError('argument str must be a string'); } var obj = {}; var opt = options || {}; var pairs = str.split(/; */); var dec = opt.decode || decode; pairs.forEach(function (pair) { var eq_idx = pair.indexOf('='); // skip things that don't look like key=value if (eq_idx < 0) { return; } var key = pair.substr(0, eq_idx).trim(); var val = pair.substr(++eq_idx, pair.length).trim(); // quoted values if ('"' == val[0]) { val = val.slice(1, -1); } // only assign once if (undefined == obj[key]) { obj[key] = tryDecode(val, dec); } }); return obj; } var Cookies = { parse: parse }; function createPreviewResolver(token, documentId, getDocByID) { var resolve = function (linkResolver, defaultUrl, cb) { if (documentId && getDocByID) { return getDocByID(documentId, { ref: token }).then(function (document) { if (!document) { cb && cb(null, defaultUrl); return defaultUrl; } else { var url = (linkResolver && linkResolver(document)) || document.url || defaultUrl; cb && cb(null, url); return url; } }); } else { return Promise.resolve(defaultUrl); } }; return { token: token, documentId: documentId, resolve: resolve }; } var PREVIEW_COOKIE = 'io.prismic.preview'; var EXPERIMENT_COOKIE = 'io.prismic.experiment'; var ResolvedApi = /** @class */ (function () { function ResolvedApi(data, httpClient, options) { this.data = data; this.masterRef = data.refs.filter(function (ref) { return ref.isMasterRef; })[0]; this.experiments = new Experiments(data.experiments); this.bookmarks = data.bookmarks; this.httpClient = httpClient; this.options = options; this.refs = data.refs; this.tags = data.tags; this.types = data.types; this.languages = data.languages; } /** * Returns a useable form from its id, as described in the RESTful description of the API. * For instance: api.form("everything") works on every repository (as "everything" exists by default) * You can then chain the calls: api.form("everything").query('[[:d = at(document.id, "UkL0gMuvzYUANCpf")]]').ref(ref).submit() */ ResolvedApi.prototype.form = function (formId) { var form = this.data.forms[formId]; if (form) { return new SearchForm(form, this.httpClient); } return null; }; ResolvedApi.prototype.everything = function () { var f = this.form('everything'); if (!f) throw new Error('Missing everything form'); return f; }; /** * The ID of the master ref on this prismic.io API. * Do not use like this: searchForm.ref(api.master()). * Instead, set your ref once in a variable, and call it when you need it; this will allow to change the ref you're viewing easily for your entire page. */ ResolvedApi.prototype.master = function () { return this.masterRef.ref; }; /** * Returns the ref ID for a given ref's label. * Do not use like this: searchForm.ref(api.ref("Future release label")). * Instead, set your ref once in a variable, and call it when you need it; this will allow to change the ref you're viewing easily for your entire page. */ ResolvedApi.prototype.ref = function (label) { var ref = this.data.refs.filter(function (ref) { return ref.label === label; })[0]; return ref ? ref.ref : null; }; ResolvedApi.prototype.currentExperiment = function () { return this.experiments.current(); }; /** * Query the repository */ ResolvedApi.prototype.query = function (q, optionsOrCallback, cb) { if (cb === void 0) { cb = function () { }; } var _a = typeof optionsOrCallback === 'function' ? { options: {}, callback: optionsOrCallback } : { options: optionsOrCallback || {}, callback: cb }, options = _a.options, callback = _a.callback; var form = this.everything(); for (var key in options) { form = form.set(key, options[key]); } if (!options.ref) { // Look in cookies if we have a ref (preview or experiment) var cookieString = ''; if (this.options.req) { // NodeJS cookieString = this.options.req.headers['cookie'] || ''; } else if (typeof window !== 'undefined' && window.document) { // Browser cookieString = window.document.cookie || ''; } var cookies = Cookies.parse(cookieString); var previewRef = cookies[PREVIEW_COOKIE]; var experimentRef = this.experiments.refFromCookie(cookies[EXPERIMENT_COOKIE]); form = form.ref(previewRef || experimentRef || this.masterRef.ref); } if (q) { form.query(q); } return form.submit(callback); }; /** * Retrieve the document returned by the given query * @param {string|array|Predicate} the query * @param {object} additional parameters. In NodeJS, pass the request as 'req'. * @param {function} callback(err, doc) */ ResolvedApi.prototype.queryFirst = function (q, optionsOrCallback, cb) { var _a = typeof optionsOrCallback === 'function' ? { options: {}, callback: optionsOrCallback } : { options: optionsOrCallback || {}, callback: cb || (function () { }) }, options = _a.options, callback = _a.callback; options.page = 1; options.pageSize = 1; return this.query(q, options).then(function (response) { var document = response && response.results && response.results[0]; callback(null, document); return document; }).catch(function (error) { callback(error); throw error; }); }; /** * Retrieve the document with the given id */ ResolvedApi.prototype.getByID = function (id, maybeOptions, cb) { var options = maybeOptions ? __assign({}, maybeOptions) : {}; if (!options.lang) options.lang = '*'; return this.queryFirst(Predicates.at('document.id', id), options, cb); }; /** * Retrieve multiple documents from an array of id */ ResolvedApi.prototype.getByIDs = function (ids, maybeOptions, cb) { var options = maybeOptions ? __assign({}, maybeOptions) : {}; if (!options.lang) options.lang = '*'; return this.query(Predicates.in('document.id', ids), options, cb); }; /** * Retrieve the document with the given uid */ ResolvedApi.prototype.getByUID = function (type, uid, maybeOptions, cb) { var options = maybeOptions ? __assign({}, maybeOptions) : {}; if (options.lang === '*') throw new Error("FORBIDDEN. You can't use getByUID with *, use the predicates instead."); if (!options.page) options.page = 1; return this.queryFirst(Predicates.at("my." + type + ".uid", uid), options, cb); }; /** * Retrieve the singleton document with the given type */ ResolvedApi.prototype.getSingle = function (type, maybeOptions, cb) { var options = maybeOptions ? __assign({}, maybeOptions) : {}; return this.queryFirst(Predicates.at('document.type', type), options, cb); }; /** * Retrieve the document with the given bookmark */ ResolvedApi.prototype.getBookmark = function (bookmark, maybeOptions, cb) { var id = this.data.bookmarks[bookmark]; if (id) { return this.getByID(id, maybeOptions, cb); } else { return Promise.reject('Error retrieving bookmarked id'); } }; ResolvedApi.prototype.getPreviewResolver = function (token, documentId) { return createPreviewResolver(token, documentId, this.getByID.bind(this)); }; ResolvedApi.prototype.previewSession = function (token, linkResolver, defaultUrl, cb) { var _this = this; console.warn('previewSession function is deprecated in favor of getPreviewResolver function.'); return new Promise(function (resolve, reject) { _this.httpClient.request(token, function (e, result) { if (e) { cb && cb(e); reject(e); } else if (result) { if (!result.mainDocument) { cb && cb(null, defaultUrl); resolve(defaultUrl); } else { return _this.getByID(result.mainDocument, { ref: token }).then(function (document) { if (!document) { cb && cb(null, defaultUrl); resolve(defaultUrl); } else { var url = (linkResolver && linkResolver(document)) || document.url || defaultUrl; cb && cb(null, url); resolve(url); } }).catch(reject); } } }); }); }; return ResolvedApi; }()); /* eslint-disable */ /** * A doubly linked list-based Least Recently Used (LRU) cache. Will keep most * recently used items while discarding least recently used items when its limit * is reached. * * Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/> * Typescript-ified by Oleksandr Nikitin <https://tvori.info> * * Illustration of the design: * * entry entry entry entry * ______ ______ ______ ______ * | head |.newer => | |.newer => | |.newer => | tail | * | A | | B | | C | | D | * |______| <= older.|______| <= older.|______| <= older.|______| * * removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added */ function MakeLRUCache(limit) { return new LRUCache(limit); } function LRUCache(limit) { // Current size of the cache. (Read-only). this.size = 0; // Maximum number of items this cache can hold. this.limit = limit; this._keymap = {}; } /** * Put <value> into the cache associated with <key>. Returns the entry which was * removed to make room for the new entry. Otherwise undefined is returned * (i.e. if there was enough room already). */ LRUCache.prototype.put = function (key, value) { var entry = { key: key, value: value }; // Note: No protection agains replacing, and thus orphan entries. By design. this._keymap[key] = entry; if (this.tail) { // link previous tail to the new tail (entry) this.tail.newer = entry; entry.older = this.tail; } else { // we're first in -- yay this.head = entry; } // add new entry to the end of the linked list -- it's now the freshest entry. this.tail = entry; if (this.size === this.limit) { // we hit the limit -- remove the head return this.shift(); } else { // increase the size counter this.size++; } }; /** * Purge the least recently used (oldest) entry from the cache. Returns the * removed entry or undefined if the cache was empty. * * If you need to perform any form of finalization of purged items, this is a * good place to do it. Simply override/replace this function: * * var c = new LRUCache(123); * c.shift = function() { * var entry = LRUCache.prototype.shift.call(this); * doSomethingWith(entry); * return entry; * } */ LRUCache.prototype.shift = function () { // todo: handle special case when limit == 1 var entry = this.head; if (entry) { if (this.head.newer) { this.head = this.head.newer; this.head.older = undefined; } else { this.head = undefined; } // Remove last strong reference to <entry> and remove links from the purged // entry being returned: entry.newer = entry.older = undefined; // delete is slow, but we need to do this to avoid uncontrollable growth: delete this._keymap[entry.key]; } console.log('purging ', entry.key); return entry; }; /** * Get and register recent use of <key>. Returns the value associated with <key> * or undefined if not in cache. */ LRUCache.prototype.get = function (key, returnEntry) { // First, find our cache entry var entry = this._keymap[key]; if (entry === undefined) return; // Not cached. Sorry. // As <key> was found in the cache, register it as being requested recently if (entry === this.tail) { // Already the most recently used entry, so no need to update the list return returnEntry ? entry : entry.value; } // HEAD--------------TAIL // <.older .newer> // <--- add direction -- // A B C <D> E if (entry.newer) { if (entry === this.head) this.head = entry.newer; entry.newer.older = entry.older; // C <-- E. } if (entry.older) entry.older.newer = entry.newer; // C. --> E entry.newer = undefined; // D --x entry.older = this.tail; // D. --> E if (this.tail) this.tail.newer = entry; // E. <-- D this.tail = entry; return returnEntry ? entry : entry.value; }; // ---------------------------------------------------------------------------- // Following code is optional and can be removed without breaking the core // functionality. /** * Check if <key> is in the cache without registering recent use. Feasible if * you do not want to chage the state of the cache, but only "peek" at it. * Returns the entry associated with <key> if found, or undefined if not found. */ LRUCache.prototype.find = function (key) { return this._keymap[key]; }; /** * Update the value of entry with <key>. Returns the old value, or undefined if * entry was not in the cache. */ LRUCache.prototype.set = function (key, value) { var oldvalue; var entry = this.get(key, true); if (entry) { oldvalue = entry.value; entry.value = value; } else { oldvalue = this.put(key, value); if (oldvalue) oldvalue = oldvalue.value; } return oldvalue; }; /** * Remove entry <key> from cache and return its value. Returns undefined if not * found. */ LRUCache.prototype.remove = function (key) { var entry = this._keymap[key]; if (!entry) return; delete this._keymap[entry.key]; // need to do delete unfortunately if (entry.newer && entry.older) { // relink the older entry with the newer entry entry.older.newer = entry.newer; entry.newer.older = entry.older; } else if (entry.newer) { // remove the link to us entry.newer.older = undefined; // link the newer entry to head this.head = entry.newer; } else if (entry.older) { // remove the link to us entry.older.newer = undefined; // link the newer entry to head this.tail = entry.older; } else { // if(entry.older === undefined && entry.newer === undefined) { this.head = this.tail = undefined; } this.size--; return entry.value; }; /** Removes all entries */ LRUCache.prototype.removeAll = function () { // This should be safe, as we never expose strong refrences to the outside this.head = this.tail = undefined; this.size = 0; this._keymap = {}; }; /** * Return an array containing all keys of entries stored in the cache object, in * arbitrary order. */ if (typeof Object.keys === 'function') { LRUCache.prototype.keys = function () { return Object.keys(this._keymap); }; } else { LRUCache.prototype.keys = function () { var keys = []; for (var k in this._keymap) keys.push(k); return keys; }; } /** * Call `fun` for each entry. Starting with the newest entry if `desc` is a true * value, otherwise starts with the oldest (head) enrty and moves towards the * tail. * * `fun` is called with 3 arguments in the context `context`: * `fun.call(context, Object key, Object value, LRUCache self)` */ LRUCache.prototype.forEach = function (fun, context, desc) { var entry; if (context === true) { desc = true; context = undefined; } else if (typeof context !== 'object') context = this; if (desc) { entry = this.tail; while (entry) { fun.call(context, entry.key, entry.value, this); entry = entry.older; } } else { entry = this.head; while (entry) { fun.call(context, entry.key, entry.value, this); entry = entry.newer; } } }; /** Returns a JSON (array) representation */ //LRUCache.prototype.toJSON = function () { // var s: IEntry[] = [], entry = this.head; // while (entry) { // s.push({ key: entry.key.toJSON(), value: entry.value.toJSON() }); // entry = entry.newer; // } // return s; //}; /** Returns a String representation */ LRUCache.prototype.toString = function () { var s = '', entry = this.head; while (entry) { s += String(entry.key) + ':' + entry.value; entry = entry.newer; if (entry) s += ' < '; } return s; }; // Export ourselves //if (typeof this === 'object') this.LRUCache = LRUCache; var DefaultApiCache = /** @class */ (function () { function DefaultApiCache(limit) { if (limit === void 0) { limit = 1000; } this.lru = MakeLRUCache(limit); } DefaultApiCache.prototype.isExpired = function (key) { var value = this.lru.get(key, false); if (value) { return value.expiredIn !== 0 && value.expiredIn < Date.now(); } else { return false; } }; DefaultApiCache.prototype.get = function (key, cb) { var value = this.lru.get(key, false); if (value && !this.isExpired(key)) { cb(null, value.data); } else { cb && cb(null); } }; DefaultApiCache.prototype.set = function (key, value, ttl, cb) { this.lru.remove(key); this.lru.put(key, { data: value, expiredIn: ttl ? (Date.now() + (ttl * 1000)) : 0, }); cb && cb(null); }; DefaultApiCache.prototype.remove = function (key, cb) { this.lru.remove(key); cb && cb(null); }; DefaultApiCache.prototype.clear = function (cb) { this.lru.removeAll(); cb && cb(null); }; return DefaultApiCache; }()); function fetchRequest(url, options, callback) { var fetchOptions = { headers: { Accept: 'application/json', }, }; if (options && options.proxyAgent) { fetchOptions.agent = options.proxyAgent; } // can't use number because of NodeJS globals included var timeoutId; var fetchPromise = crossFetch(url, fetchOptions); var promise = options.timeoutInMs ? Promise.race([ fetchPromise, new Promise(function (_, reject) { timeoutId = setTimeout(function () { return reject(new Error(url + " response timeout")); }, options.timeoutInMs); }) ]) : fetchPromise; promise.then(function (resp) { clearTimeout(timeoutId); if (~~(resp.status / 100 !== 2)) { /** * @description * drain the resp before throwing an error to prevent memory leaks * @link https://github.com/bitinn/node-fetch/issues/83 */ return resp.text().then(function () { var e = new Error("Unexpected status code [" + resp.status + "] on URL " + url); e.status = resp.status; throw e; }); } return resp.json().then(function (result) { var cacheControl = resp.headers.get('cache-control'); var parsedCacheControl = cacheControl ? /max-age=(\d+)/.exec(cacheControl) : null; var ttl = parsedCacheControl ? parseInt(parsedCacheControl[1], 10) : undefined; callback(null, result, resp, ttl); }); }).catch(function (err) { clearTimeout(timeoutId); callback(err); }); } var DefaultRequestHandler = /** @class */ (function () { function DefaultRequestHandler(options) { this.options = options || {}; } DefaultRequestHandler.prototype.request = function (url, callback) { fetchRequest(url, this.options, callback); }; return DefaultRequestHandler; }()); var HttpClient = /** @class */ (function () { function HttpClient(requestHandler, cache, proxyAgent, timeoutInMs) { this.requestHandler = requestHandler || new DefaultRequestHandler({ proxyAgent: proxyAgent, timeoutInMs: timeoutInMs }); this.cache = cache || new DefaultApiCache(); } HttpClient.prototype.request = function (url, callback) { this.requestHandler.request(url, function (err, result, xhr, ttl) { if (err) { callback && callback(err, null, xhr, ttl); } else if (result) { callback && callback(null, result, xhr, ttl); } }); }; /** * Fetch a URL corresponding to a query, and parse the response as a Response object */ HttpClient.prototype.cachedRequest = function (url, maybeOptions) { var _this = this; var options = maybeOptions || {}; var run = function (cb) { var cacheKey = options.cacheKey || url; _this.cache.get(cacheKey, function (cacheGetError, cacheGetValue) { if (cacheGetError || cacheGetValue) { cb(cacheGetError, cacheGetValue); } else { _this.request(url, function (fetchError, fetchValue, _, ttlReq) { if (fetchError) { cb(fetchError, null); } else { var ttl = ttlReq || options.ttl; if (ttl) { _this.cache.set(cacheKey, fetchValue, ttl, cb); } cb(null, fetchValue); } }); } }); }; return new Promise(function (resolve, reject) { run(function (err, value) { if (err) reject(err); if (value) resolve(value); }); }); }; return HttpClient; }()); function separator(url) { return url.indexOf('?') > -1 ? '&' : '?'; } var Api = /** @class */ (function () { function Api(url, options) { this.options = options || {}; this.url = url; if (this.options.accessToken) { var accessTokenParam = "access_token=" + this.options.accessToken; this.url += separator(url) + accessTokenParam; } if (this.options.routes) { this.url += separator(url) + ("routes=" + encodeURIComponent(JSON.stringify(this.options.routes))); } this.apiDataTTL = this.options.apiDataTTL || 5; this.httpClient = new HttpClient(this.options.requestHandler, this.options.apiCache, this.options.proxyAgent, this.options.timeoutInMs); } /** * Fetches data used to construct the api client, from cache if it's * present, otherwise from calling the prismic api endpoint (which is * then cached). */ Api.prototype.get = function (cb) { var _this = this; return this.httpClient.cachedRequest(this.url, { ttl: this.apiDataTTL }).then(function (data) { var resolvedApi = new ResolvedApi(data, _this.httpClient, _this.options); cb && cb(null, resolvedApi); return resolvedApi; }).catch(function (error) { cb && cb(error); throw error; }); }; return Api; }()); var DefaultClient = /** @class */ (function () { function DefaultClient(url, options) { this.api = new Api(url, options); } DefaultClient.prototype.getApi = function () { return this.api.get(); }; DefaultClient.prototype.everything = function () { return this.form('everything'); }; DefaultClient.prototype.form = function (formId) { return new LazySearchForm(formId, this.api); }; DefaultClient.prototype.query = function (q, optionsOrCallback, cb) { return this.getApi().then(function (api) { return api.query(q, optionsOrCallback, cb); }); }; DefaultClient.prototype.queryFirst = function (q, optionsOrCallback, cb) { return this.getApi().then(function (api) { return api.queryFirst(q, optionsOrCallback, cb); }); }; DefaultClient.prototype.getByID = function (id, options, cb) { return this.getApi().then(function (api) { return api.getByID(id, options, cb); }); }; DefaultClient.prototype.getByIDs = function (ids, options, cb) { return this.getApi().then(function (api) { return api.getByIDs(ids, options, cb); }); }; DefaultClient.prototype.getByUID = function (type, uid, options, cb) { return this.getApi().then(function (api) { return api.getByUID(type, uid, options, cb); }); }; DefaultClient.prototype.getSingle = function (type, options, cb) { return this.getApi().then(function (api) { return api.getSingle(type, options, cb); }); }; DefaultClient.prototype.getBookmark