@builder.io/sdk
Version:
This SDK is largely a wrapper over our [Content API](https://www.builder.io/c/docs/content-api)
1,221 lines (1,186 loc) • 128 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define('@builder.io/sdk', ['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BuilderIO = {}));
})(this, (function (exports) { 'use strict';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
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);
};
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
(function () {
if (typeof window === 'undefined' || typeof window.CustomEvent === 'function') return false;
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: null };
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
window.CustomEvent = CustomEvent;
})();
var isSafari = typeof window !== 'undefined' &&
/^((?!chrome|android).)*safari/i.test(window.navigator.userAgent);
var isClient = typeof window !== 'undefined';
// TODO: queue all of these in a debounceNextTick
function nextTick(fn) {
// if (typeof process !== 'undefined' && process.nextTick) {
// console.log('process.nextTick?');
// process.nextTick(fn);
// return;
// }
// FIXME: fix the real safari issue of this randomly not working
if (!isClient || isSafari || typeof MutationObserver === 'undefined') {
setTimeout(fn);
return;
}
var called = 0;
var observer = new MutationObserver(function () { return fn(); });
var element = document.createTextNode('');
observer.observe(element, {
characterData: true,
});
// tslint:disable-next-line
element.data = String((called = ++called));
}
var PROPERTY_NAME_DENY_LIST = Object.freeze(['__proto__', 'prototype', 'constructor']);
// TODO: unit tests
var QueryString = /** @class */ (function () {
function QueryString() {
}
QueryString.parseDeep = function (queryString) {
var obj = this.parse(queryString);
return this.deepen(obj);
};
QueryString.stringifyDeep = function (obj) {
var map = this.flatten(obj);
return this.stringify(map);
};
QueryString.parse = function (queryString) {
var query = {};
var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
// TODO: node support?
try {
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
}
catch (error) {
// Ignore malformed URI components
}
}
return query;
};
QueryString.stringify = function (map) {
var str = '';
for (var key in map) {
if (map.hasOwnProperty(key)) {
var value = map[key];
if (str) {
str += '&';
}
str += encodeURIComponent(key) + '=' + encodeURIComponent(value);
}
}
return str;
};
QueryString.deepen = function (map) {
// FIXME; Should be type Tree = Record<string, string | Tree>
// requires a typescript upgrade.
var output = {};
for (var k in map) {
var t = output;
var parts = k.split('.');
var key = parts.pop();
for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {
var part = parts_1[_i];
assertAllowedPropertyName(part);
t = t[part] = t[part] || {};
}
t[key] = map[k];
}
return output;
};
QueryString.flatten = function (obj, _current, _res) {
if (_res === void 0) { _res = {}; }
for (var key in obj) {
var value = obj[key];
var newKey = _current ? _current + '.' + key : key;
if (value && typeof value === 'object') {
this.flatten(value, newKey, _res);
}
else {
_res[newKey] = value;
}
}
return _res;
};
return QueryString;
}());
function assertAllowedPropertyName(name) {
if (PROPERTY_NAME_DENY_LIST.indexOf(name) >= 0)
throw new Error("Property name \"".concat(name, "\" is not allowed"));
}
var Subscription = /** @class */ (function () {
function Subscription(listeners, listener) {
this.listeners = listeners;
this.listener = listener;
this.unsubscribed = false;
this.otherSubscriptions = [];
}
Object.defineProperty(Subscription.prototype, "closed", {
get: function () {
return this.unsubscribed;
},
enumerable: false,
configurable: true
});
Subscription.prototype.add = function (subscription) {
this.otherSubscriptions.push(subscription);
};
Subscription.prototype.unsubscribe = function () {
if (this.unsubscribed) {
return;
}
if (this.listener && this.listeners) {
var index = this.listeners.indexOf(this.listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
this.otherSubscriptions.forEach(function (sub) { return sub.unsubscribe(); });
this.unsubscribed = true;
};
return Subscription;
}());
// TODO: follow minimal basic spec: https://github.com/tc39/proposal-observable
var BehaviorSubject = /** @class */ (function () {
function BehaviorSubject(value) {
var _this = this;
this.value = value;
this.listeners = [];
this.errorListeners = [];
this.then = function () {
var _a;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return (_a = _this.toPromise()).then.apply(_a, args);
};
}
BehaviorSubject.prototype.next = function (value) {
this.value = value;
for (var _i = 0, _a = this.listeners; _i < _a.length; _i++) {
var listener = _a[_i];
listener(value);
}
};
// TODO: implement this as PIPE instead
BehaviorSubject.prototype.map = function (fn) {
var newSubject = new BehaviorSubject(fn(this.value));
// TODO: on destroy delete these
this.subscribe(function (val) {
newSubject.next(fn(val));
});
this.catch(function (err) {
newSubject.error(err);
});
return newSubject;
};
BehaviorSubject.prototype.catch = function (errorListener) {
this.errorListeners.push(errorListener);
return new Subscription(this.errorListeners, errorListener);
};
BehaviorSubject.prototype.error = function (error) {
for (var _i = 0, _a = this.errorListeners; _i < _a.length; _i++) {
var listener = _a[_i];
listener(error);
}
};
BehaviorSubject.prototype.subscribe = function (listener, errorListener) {
this.listeners.push(listener);
if (errorListener) {
this.errorListeners.push(errorListener);
}
return new Subscription(this.listeners, listener);
};
BehaviorSubject.prototype.toPromise = function () {
var _this = this;
return new Promise(function (resolve, reject) {
var subscription = _this.subscribe(function (value) {
resolve(value);
subscription.unsubscribe();
}, function (err) {
reject(err);
subscription.unsubscribe();
});
});
};
BehaviorSubject.prototype.promise = function () {
return this.toPromise();
};
return BehaviorSubject;
}());
var State = {
Pending: 'Pending',
Fulfilled: 'Fulfilled',
Rejected: 'Rejected',
};
function isFunction(val) {
return val && typeof val === 'function';
}
function isObject(val) {
return val && typeof val === 'object';
}
var TinyPromise = /** @class */ (function () {
function TinyPromise(executor) {
this._state = State.Pending;
this._handlers = [];
this._value = null;
executor(this._resolve.bind(this), this._reject.bind(this));
}
TinyPromise.prototype._resolve = function (x) {
var _this = this;
if (x instanceof TinyPromise) {
x.then(this._resolve.bind(this), this._reject.bind(this));
}
else if (isObject(x) || isFunction(x)) {
var called_1 = false;
try {
var thenable = x.then;
if (isFunction(thenable)) {
thenable.call(x, function (result) {
if (!called_1)
_this._resolve(result);
called_1 = true;
return undefined;
}, function (error) {
if (!called_1)
_this._reject(error);
called_1 = true;
return undefined;
});
}
else {
this._fulfill(x);
}
}
catch (ex) {
if (!called_1) {
this._reject(ex);
}
}
}
else {
this._fulfill(x);
}
};
TinyPromise.prototype._fulfill = function (result) {
var _this = this;
this._state = State.Fulfilled;
this._value = result;
this._handlers.forEach(function (handler) { return _this._callHandler(handler); });
};
TinyPromise.prototype._reject = function (error) {
var _this = this;
this._state = State.Rejected;
this._value = error;
this._handlers.forEach(function (handler) { return _this._callHandler(handler); });
};
TinyPromise.prototype._isPending = function () {
return this._state === State.Pending;
};
TinyPromise.prototype._isFulfilled = function () {
return this._state === State.Fulfilled;
};
TinyPromise.prototype._isRejected = function () {
return this._state === State.Rejected;
};
TinyPromise.prototype._addHandler = function (onFulfilled, onRejected) {
this._handlers.push({
onFulfilled: onFulfilled,
onRejected: onRejected,
});
};
TinyPromise.prototype._callHandler = function (handler) {
if (this._isFulfilled() && isFunction(handler.onFulfilled)) {
handler.onFulfilled(this._value);
}
else if (this._isRejected() && isFunction(handler.onRejected)) {
handler.onRejected(this._value);
}
};
TinyPromise.prototype.then = function (onFulfilled, onRejected) {
var _this = this;
switch (this._state) {
case State.Pending: {
return new TinyPromise(function (resolve, reject) {
_this._addHandler(function (value) {
nextTick(function () {
try {
if (isFunction(onFulfilled)) {
resolve(onFulfilled(value));
}
else {
resolve(value);
}
}
catch (ex) {
reject(ex);
}
});
}, function (error) {
nextTick(function () {
try {
if (isFunction(onRejected)) {
resolve(onRejected(error));
}
else {
reject(error);
}
}
catch (ex) {
reject(ex);
}
});
});
});
}
case State.Fulfilled: {
return new TinyPromise(function (resolve, reject) {
nextTick(function () {
try {
if (isFunction(onFulfilled)) {
resolve(onFulfilled(_this._value));
}
else {
resolve(_this._value);
}
}
catch (ex) {
reject(ex);
}
});
});
}
case State.Rejected: {
return new TinyPromise(function (resolve, reject) {
nextTick(function () {
try {
if (isFunction(onRejected)) {
resolve(onRejected(_this._value));
}
else {
reject(_this._value);
}
}
catch (ex) {
reject(ex);
}
});
});
}
}
};
return TinyPromise;
}());
var Promise$1 = (typeof Promise !== 'undefined' ? Promise : TinyPromise);
// Webpack workaround to conditionally require certain external modules
// only on the server and not bundle them on the client
var serverOnlyRequire;
try {
// tslint:disable-next-line:no-eval
serverOnlyRequire = eval('require');
}
catch (err) {
// all good
serverOnlyRequire = (function () { return null; });
}
var serverOnlyRequire$1 = serverOnlyRequire;
function promiseResolve(value) {
return new Promise$1(function (resolve) { return resolve(value); });
}
// Adapted from https://raw.githubusercontent.com/developit/unfetch/master/src/index.mjs
function tinyFetch(url, options) {
if (options === void 0) { options = {}; }
return new Promise$1(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open(options.method || 'get', url, true);
if (options.headers) {
for (var i in options.headers) {
request.setRequestHeader(i, options.headers[i]);
}
}
request.withCredentials = options.credentials === 'include';
request.onload = function () {
resolve(response());
};
request.onerror = reject;
request.send(options.body);
function response() {
var keys = [];
var all = [];
var headers = {};
var header = undefined;
request
.getAllResponseHeaders()
.replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, function (_match, _key, value) {
var key = _key;
keys.push((key = key.toLowerCase()));
all.push([key, value]);
header = headers[key];
headers[key] = header ? "".concat(header, ",").concat(value) : value;
return '';
});
return {
ok: ((request.status / 100) | 0) === 2,
status: request.status,
statusText: request.statusText,
url: request.responseURL,
clone: response,
text: function () { return promiseResolve(request.responseText); },
json: function () { return promiseResolve(request.responseText).then(JSON.parse); },
blob: function () { return promiseResolve(new Blob([request.response])); },
headers: {
keys: function () { return keys; },
entries: function () { return all; },
get: function (n) { return headers[n.toLowerCase()]; },
has: function (n) { return n.toLowerCase() in headers; },
},
};
}
});
}
function getFetch() {
// If fetch is defined, in the browser, via polyfill, or in a Cloudflare worker, use it.
var _fetch = undefined;
if (globalThis.fetch) {
_fetch !== null && _fetch !== void 0 ? _fetch : (_fetch = globalThis.fetch);
}
else if (typeof window === 'undefined') {
// If fetch is not defined, in a Node.js environment, use node-fetch.
try {
// node-fetch@^3 is ESM only, and will throw error on require.
_fetch !== null && _fetch !== void 0 ? _fetch : (_fetch = serverOnlyRequire$1('node-fetch'));
}
catch (e) {
// If node-fetch is not installed, use tiny-fetch.
console.warn('node-fetch is not installed. consider polyfilling fetch or installing node-fetch.');
console.warn(e);
}
}
// Otherwise, use tiny-fetch.
return _fetch !== null && _fetch !== void 0 ? _fetch : tinyFetch;
}
function assign(target) {
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
// Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
}
function throttle(func, wait, options) {
if (options === void 0) { options = {}; }
var context;
var args;
var result;
var timeout = null;
var previous = 0;
var later = function () {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout)
context = args = null;
};
return function () {
var now = Date.now();
if (!previous && options.leading === false)
previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout)
context = args = null;
}
else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
var camelCaseToKebabCase = function (str) {
return str ? str.replace(/([A-Z])/g, function (g) { return "-".concat(g[0].toLowerCase()); }) : '';
};
var Animator = /** @class */ (function () {
function Animator() {
}
Animator.prototype.bindAnimations = function (animations) {
for (var _i = 0, animations_1 = animations; _i < animations_1.length; _i++) {
var animation = animations_1[_i];
switch (animation.trigger) {
case 'pageLoad':
this.triggerAnimation(animation);
break;
case 'hover':
this.bindHoverAnimation(animation);
break;
case 'scrollInView':
this.bindScrollInViewAnimation(animation);
break;
}
}
};
Animator.prototype.warnElementNotPresent = function (id) {
console.warn("Cannot animate element: element with ID ".concat(id, " not found!"));
};
Animator.prototype.augmentAnimation = function (animation, element) {
var stylesUsed = this.getAllStylesUsed(animation);
var computedStyle = getComputedStyle(element);
// const computedStyle = getComputedStyle(element);
// // FIXME: this will break if original load is in one reponsive size then resize to another hmmm
// Need to use transform instead of left since left can change on screen sizes
var firstStyles = animation.steps[0].styles;
var lastStyles = animation.steps[animation.steps.length - 1].styles;
var bothStyles = [firstStyles, lastStyles];
// FIXME: this won't work as expected for augmented animations - may need the editor itself to manage this
for (var _i = 0, bothStyles_1 = bothStyles; _i < bothStyles_1.length; _i++) {
var styles = bothStyles_1[_i];
for (var _a = 0, stylesUsed_1 = stylesUsed; _a < stylesUsed_1.length; _a++) {
var style = stylesUsed_1[_a];
if (!(style in styles)) {
styles[style] = computedStyle[style];
}
}
}
};
Animator.prototype.getAllStylesUsed = function (animation) {
var properties = [];
for (var _i = 0, _a = animation.steps; _i < _a.length; _i++) {
var step = _a[_i];
for (var key in step.styles) {
if (properties.indexOf(key) === -1) {
properties.push(key);
}
}
}
return properties;
};
Animator.prototype.triggerAnimation = function (animation) {
var _this = this;
// TODO: do for ALL elements
var elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
if (!elements.length) {
this.warnElementNotPresent(animation.elementId || animation.id || '');
return;
}
Array.from(elements).forEach(function (element) {
_this.augmentAnimation(animation, element);
// TODO: do this properly, may have other animations of different properties
// TODO: only override the properties
// TODO: if there is an entrance and hover animation, the transition duration will get effed
// element.setAttribute('style', '');
// const styledUsed = this.getAllStylesUsed(animation);
element.style.transition = 'none';
element.style.transitionDelay = '0';
assign(element.style, animation.steps[0].styles);
// TODO: queue/batch these timeouts
// TODO: only include properties explicitly set in the animation
// using Object.keys(styles)
setTimeout(function () {
element.style.transition = "all ".concat(animation.duration, "s ").concat(camelCaseToKebabCase(animation.easing));
if (animation.delay) {
element.style.transitionDelay = animation.delay + 's';
}
assign(element.style, animation.steps[1].styles);
// TODO: maybe remove/reset transitoin property after animation duration
// TODO: queue timers
setTimeout(function () {
// TODO: what if has other transition (reset back to what it was)
element.style.transition = '';
element.style.transitionDelay = '';
}, (animation.delay || 0) * 1000 + animation.duration * 1000 + 100);
});
});
};
Animator.prototype.bindHoverAnimation = function (animation) {
var _this = this;
// TODO: is it multiple binding when editing...?
// TODO: unbind on element remove
// TODO: apply to ALL elements
var elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
if (!elements.length) {
this.warnElementNotPresent(animation.elementId || animation.id || '');
return;
}
Array.from(elements).forEach(function (element) {
_this.augmentAnimation(animation, element);
var defaultState = animation.steps[0].styles;
var hoverState = animation.steps[1].styles;
function attachDefaultState() {
assign(element.style, defaultState);
}
function attachHoverState() {
assign(element.style, hoverState);
}
attachDefaultState();
element.addEventListener('mouseenter', attachHoverState);
element.addEventListener('mouseleave', attachDefaultState);
// TODO: queue/batch these timeouts
setTimeout(function () {
element.style.transition = "all ".concat(animation.duration, "s ").concat(camelCaseToKebabCase(animation.easing));
if (animation.delay) {
element.style.transitionDelay = animation.delay + 's';
}
});
});
};
// TODO: unbind on element remove
Animator.prototype.bindScrollInViewAnimation = function (animation) {
var _this = this;
// TODO: apply to ALL matching elements
var elements = Array.prototype.slice.call(document.getElementsByClassName(animation.elementId || animation.id || ''));
if (!elements.length) {
this.warnElementNotPresent(animation.elementId || animation.id || '');
return;
}
// TODO: if server side rendered and scrolled into view don't animate...
Array.from(elements).forEach(function (element) {
_this.augmentAnimation(animation, element);
var triggered = false;
var pendingAnimation = false;
function immediateOnScroll() {
if (!triggered && isScrolledIntoView(element)) {
triggered = true;
pendingAnimation = true;
setTimeout(function () {
assign(element.style, animation.steps[1].styles);
if (!animation.repeat) {
document.removeEventListener('scroll', onScroll);
}
setTimeout(function () {
pendingAnimation = false;
if (!animation.repeat) {
element.style.transition = '';
element.style.transitionDelay = '';
}
}, (animation.duration + (animation.delay || 0)) * 1000 + 100);
});
}
else if (animation.repeat &&
triggered &&
!pendingAnimation &&
!isScrolledIntoView(element)) {
// we want to repeat the animation every time the the element is out of view and back again
triggered = false;
assign(element.style, animation.steps[0].styles);
}
}
// TODO: roll all of these in one for more efficiency of checking all the rects
var onScroll = throttle(immediateOnScroll, 200, { leading: false });
// TODO: fully in view or partially
function isScrolledIntoView(elem) {
var rect = elem.getBoundingClientRect();
var windowHeight = window.innerHeight;
var thresholdPercent = (animation.thresholdPercent || 0) / 100;
var threshold = thresholdPercent * windowHeight;
// TODO: partial in view? or what if element is larger than screen itself
return (rect.bottom > threshold && rect.top < windowHeight - threshold // Element is peeking top or bottom
// (rect.top > 0 && rect.bottom < window.innerHeight) || // element fits within the screen and is fully on screen (not hanging off at all)
// (rect.top < 0 && rect.bottom > window.innerHeight) // element is larger than the screen and hangs over the top and bottom
);
}
var defaultState = animation.steps[0].styles;
function attachDefaultState() {
assign(element.style, defaultState);
}
attachDefaultState();
// TODO: queue/batch these timeouts!
setTimeout(function () {
element.style.transition = "all ".concat(animation.duration, "s ").concat(camelCaseToKebabCase(animation.easing));
if (animation.delay) {
element.style.transitionDelay = animation.delay + 's';
}
});
// TODO: one listener for everything
document.addEventListener('scroll', onScroll, { capture: true, passive: true });
// Do an initial check
immediateOnScroll();
});
};
return Animator;
}());
/**
* RegExp to match field-content in RFC 7230 sec 3.2
*
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
* obs-text = %x80-FF
*/
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
var Cookies = /** @class */ (function () {
function Cookies(request, response) {
this.request = request;
this.response = response;
}
Cookies.prototype.get = function (name) {
var header = this.request.headers['cookie'];
if (!header) {
return;
}
var match = header.match(getPattern(name));
if (!match) {
return;
}
var value = match[1];
return value;
};
Cookies.prototype.set = function (name, value, opts) {
var res = this.response;
var req = this.request;
var headers = res.getHeader('Set-Cookie') || [];
// TODO: just make this always true
var secure = this.secure !== undefined
? !!this.secure
: req.protocol === 'https' || req.connection.encrypted;
var cookie = new Cookie(name, value, opts);
if (typeof headers === 'string') {
headers = [headers];
}
if (!secure && opts && opts.secure) {
throw new Error('Cannot send secure cookie over unencrypted connection');
}
cookie.secure = secure;
if (opts && 'secure' in opts) {
cookie.secure = !!opts.secure;
}
pushCookie(headers, cookie);
var setHeader = res.setHeader;
setHeader.call(res, 'Set-Cookie', headers);
return this;
};
return Cookies;
}());
var Cookie = /** @class */ (function () {
function Cookie(name, value, attrs) {
this.path = '/';
this.domain = undefined;
this.httpOnly = true;
this.sameSite = false;
this.secure = false;
this.overwrite = false;
this.name = '';
this.value = '';
if (!fieldContentRegExp.test(name)) {
throw new TypeError('argument name is invalid');
}
if (value && !fieldContentRegExp.test(value)) {
throw new TypeError('argument value is invalid');
}
if (!value) {
this.expires = new Date(0);
}
this.name = name;
this.value = value || '';
if (attrs.expires) {
this.expires = attrs.expires;
}
if (attrs.secure) {
this.secure = attrs.secure;
}
}
Cookie.prototype.toString = function () {
return "".concat(this.name, "=").concat(this.value);
};
Cookie.prototype.toHeader = function () {
var header = this.toString();
if (this.maxAge) {
this.expires = new Date(Date.now() + this.maxAge);
}
if (this.path) {
header += "; path=".concat(this.path);
}
if (this.expires) {
header += "; expires=".concat(this.expires.toUTCString());
}
if (this.domain) {
header += "; domain=".concat(this.domain);
}
// TODO: samesite=none by default (?)
header += "; SameSite=".concat(this.sameSite === true ? 'strict' : 'None');
// TODO: On by default
if (this.secure) {
header += '; secure';
}
if (this.httpOnly) {
header += '; httponly';
}
return header;
};
return Cookie;
}());
function getPattern(name) {
return new RegExp("(?:^|;) *".concat(name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), "=([^;]*)"));
}
function pushCookie(headers, cookie) {
if (cookie.overwrite) {
for (var i = headers.length - 1; i >= 0; i--) {
if (headers[i].indexOf("".concat(cookie.name, "=")) === 0) {
headers.splice(i, 1);
}
}
}
headers.push(cookie.toHeader());
}
function omit(obj) {
var values = [];
for (var _i = 1; _i < arguments.length; _i++) {
values[_i - 1] = arguments[_i];
}
var newObject = Object.assign({}, obj);
for (var _a = 0, values_1 = values; _a < values_1.length; _a++) {
var key = values_1[_a];
delete newObject[key];
}
return newObject;
}
/**
* @credit https://stackoverflow.com/a/2117523
*/
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* Slightly cleaner and smaller UUIDs
*/
function uuid() {
return uuidv4().replace(/-/g, '');
}
function emptyUrl() {
return {
query: null,
port: null,
auth: null,
hash: null,
host: null,
hostname: null,
href: null,
path: null,
pathname: null,
protocol: null,
search: null,
slashes: null,
};
}
// Replacement for `url.parse` using `URL` global object that works with relative paths.
// Assumptions: this function operates in a NodeJS environment.
function parse$1(url) {
var _a;
var out = emptyUrl();
var u;
var pathOnly = url === '' || url[0] === '/';
if (pathOnly) {
u = new URL(url, 'http://0.0.0.0/');
out.href = u.href;
out.href = (_a = out.href) === null || _a === void 0 ? void 0 : _a.slice(14); // remove 'http://0.0.0.0/'
}
else {
u = new URL(url);
out.href = u.href;
out.port = u.port === '' ? null : u.port;
out.hash = u.hash === '' ? null : u.hash;
out.host = u.host;
out.hostname = u.hostname;
out.href = u.href;
out.pathname = u.pathname;
out.protocol = u.protocol;
out.slashes = url[u.protocol.length] === '/'; // check if the mimetype is proceeded by a slash
}
out.search = u.search;
out.query = u.search.slice(1); // remove '?'
out.path = "".concat(u.pathname).concat(u.search);
out.pathname = u.pathname;
return out;
}
function pad (hash, len) {
while (hash.length < len) {
hash = '0' + hash;
}
return hash;
}
function fold (hash, text) {
var i;
var chr;
var len;
if (text.length === 0) {
return hash;
}
for (i = 0, len = text.length; i < len; i++) {
chr = text.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash < 0 ? hash * -2 : hash;
}
function foldObject (hash, o, seen) {
return Object.keys(o).sort().reduce(foldKey, hash);
function foldKey (hash, key) {
return foldValue(hash, o[key], key, seen);
}
}
function foldValue (input, value, key, seen) {
var hash = fold(fold(fold(input, key), toString(value)), typeof value);
if (value === null) {
return fold(hash, 'null');
}
if (value === undefined) {
return fold(hash, 'undefined');
}
if (typeof value === 'object' || typeof value === 'function') {
if (seen.indexOf(value) !== -1) {
return fold(hash, '[Circular]' + key);
}
seen.push(value);
var objHash = foldObject(hash, value, seen);
if (!('valueOf' in value) || typeof value.valueOf !== 'function') {
return objHash;
}
try {
return fold(objHash, String(value.valueOf()))
} catch (err) {
return fold(objHash, '[valueOf exception]' + (err.stack || err.message))
}
}
return fold(hash, value.toString());
}
function toString (o) {
return Object.prototype.toString.call(o);
}
function sum (o) {
return pad(foldValue(0, o, '', []).toString(16), 8);
}
var hashSum = sum;
/**
* Safe conversion to error type. Intended to be used in catch blocks where the
* value is not guaranteed to be an error.
*
* @example
* try {
* throw new Error('Something went wrong')
* }
* catch (err: unknown) {
* const error: Error = toError(err)
* }
*/
function toError(err) {
if (err instanceof Error)
return err;
return new Error(String(err));
}
var DEFAULT_API_VERSION = 'v3';
var SDK_VERSION = '6.1.0';
function datePlusMinutes(minutes) {
if (minutes === void 0) { minutes = 30; }
return new Date(Date.now() + minutes * 60000);
}
var isPositiveNumber = function (thing) {
return typeof thing === 'number' && !isNaN(thing) && thing >= 0;
};
var isReactNative = typeof navigator === 'object' && navigator.product === 'ReactNative';
var validEnvList = [
'production',
'qa',
'test',
'development',
'dev',
'cdn-qa',
'cloud',
'fast',
'cdn2',
'cdn-prod',
];
function getQueryParam(url, variable) {
var query = url.split('?')[1] || '';
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) === variable) {
return decodeURIComponent(pair[1]);
}
}
return null;
}
var urlParser = {
parse: function (url) {
var el = document.createElement('a');
el.href = url;
var out = {};
var props = [
'username',
'password',
'host',
'hostname',
'port',
'protocol',
'origin',
'pathname',
'search',
'hash',
];
for (var _i = 0, props_1 = props; _i < props_1.length; _i++) {
var prop = props_1[_i];
out[prop] = el[prop];
}
// IE 11 pathname handling workaround
// (IE omits preceeding '/', unlike other browsers)
if ((out.pathname || out.pathname === '') &&
typeof out.pathname === 'string' &&
out.pathname.indexOf('/') !== 0) {
out.pathname = '/' + out.pathname;
}
return out;
},
};
var parse = isReactNative
? function () { return emptyUrl(); }
: typeof window === 'object'
? urlParser.parse
: parse$1;
function setCookie(name, value, expires) {
try {
var expiresString = '';
// TODO: need to know if secure server side
if (expires) {
expiresString = '; expires=' + expires.toUTCString();
}
var secure = isBrowser ? location.protocol === 'https:' : true;
document.cookie =
name +
'=' +
(value || '') +
expiresString +
'; path=/' +
(secure ? '; secure; SameSite=None' : '');
}
catch (err) {
console.warn('Could not set cookie', err);
}
}
function getCookie(name) {
try {
return (decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' +
encodeURIComponent(name).replace(/[\-\.\+\*]/g, '\\$&') +
'\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null);
}
catch (err) {
console.warn('Could not get cookie', err);
}
}
function size(object) {
return Object.keys(object).length;
}
function find(target, callback) {
var list = target;
// Makes sures is always has an positive integer as length.
var length = list.length >>> 0;
var thisArg = arguments[1];
for (var i = 0; i < length; i++) {
var element = list[i];
if (callback.call(thisArg, element, i, list)) {
return element;
}
}
}
var sessionStorageKey = 'builderSessionId';
var localStorageKey = 'builderVisitorId';
var isBrowser = typeof window !== 'undefined' && !isReactNative;
var isIframe = isBrowser && window.top !== window.self;
function BuilderComponent(info) {
if (info === void 0) { info = {}; }
return Builder.Component(info);
}
var Builder = /** @class */ (function () {
function Builder(apiKey, request, response, forceNewInstance, authToken, apiVersion) {
if (apiKey === void 0) { apiKey = null; }
if (forceNewInstance === void 0) { forceNewInstance = false; }
if (authToken === void 0) { authToken = null; }
var _this = this;
this.request = request;
this.response = response;
this.eventsQueue = [];
this.throttledClearEventsQueue = throttle(function () {
_this.processEventsQueue();
// Extend the session cookie
_this.setCookie(sessionStorageKey, _this.sessionId, datePlusMinutes(30));
}, 5);
this.env = 'production';
this.sessionId = this.getSessionId();
this.targetContent = true;
this.contentPerRequest = 1;
// TODO: make array or function
this.allowCustomFonts = true;
this.cookies = null;
// TODO: api options object
this.cachebust = false;
this.overrideParams = '';
this.noCache = false;
this.preview = false;
/**
* Dictates which API endpoint is used when fetching content. Allows `'content'` and `'query'`.
* Defaults to `'query'`.
*/
this.apiEndpoint$ = new BehaviorSubject('query');
this.apiVersion$ = new BehaviorSubject(undefined);
this.canTrack$ = new BehaviorSubject(!this.browserTrackingDisabled);
this.hasOverriddenCanTrack = false;
this.apiKey$ = new BehaviorSubject(null);
this.authToken$ = new BehaviorSubject(null);
this.userAttributesChanged = new BehaviorSubject(null);
this.editingMode$ = new BehaviorSubject(isIframe);
// TODO: decorator to do this stuff with the get/set (how do with typing too? compiler?)
this.editingModel$ = new BehaviorSubject(null);
this.userAgent = (typeof navigator === 'object' && navigator.userAgent) || '';
this.trackingHooks = [];
// Set this to control the userId
// TODO: allow changing it mid session and updating existing data to be associated
// e.g. for when a user naviga