@oat-sa/tao-core-sdk
Version:
Core libraries of TAO
234 lines (223 loc) • 9.74 kB
JavaScript
define(['exports'], function (exports) { 'use strict';
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2025 Open Assessment Technologies SA;
*
* Factory for creating a cookie‐based storage utility. Each instance
* can be initialized with its own default options, but you may override
* path, domainLevel, or expires on a per‐method call as needed.
*
* @param {Object} [defaultOpts={}]
* @param {string} [defaultOpts.path='/'] - Default cookie path.
* @param {number} [defaultOpts.domainLevel] - Default number of hostname segments to include in `domain=`.
* @param {string|number|Date} [defaultOpts.expires] - Default expiration (Date, ISO string, or timestamp). Defaults to +10 years.
*
* @returns {Object} An object with methods:
* - getItem(key)
* - setItem(key, value, [opts])
* - removeItem(key, [opts])
* - keys()
* - clearAll([opts])
*/
function createCookieStorage() {
let defaultOpts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
/**
* Take the last `n` segments of window.location.hostname,
* or return null if there aren’t enough segments.
* E.g. hostname="foo.bar.example.com", level=2 → "example.com"
* @param {number} level
* @returns {string|null}
*/
function pickDomain(level) {
const parts = window.location.hostname.split('.');
if (parts.length < level) return null;
return parts.slice(-level).join('.');
}
/**
* Normalize an `expires` option that may be:
* • a Date instance
* • a parsable string/number
* • missing/undefined
* Defaults to “10 years from now” if no `expires` was provided.
*
* @param {Date|string|number|null|undefined} expires
* @returns {Date}
*/
function normalizeExpires(expires) {
if (expires instanceof Date) {
return expires;
}
if (expires) {
return new Date(expires);
}
const d = new Date();
d.setFullYear(d.getFullYear() + 10);
return d;
}
/**
* Build the cookie‐option string (expires, path, domain) from:
* 1) the factory’s default options
* 2) any per‐call overrides provided in `opts`
*
* @param {Object} [opts={}]
* @param {string} [opts.path] - Cookie path override.
* @param {number} [opts.domainLevel] - If provided, take last N segments of hostname.
* @param {string|number|Date} [opts.expires] - Expiration override. Defaults to factory’s default or +10 years.
*
* @returns {string} A string like "; expires=Thu, 01 Jan 2035 12:00:00 GMT; path=/; domain=example.com"
*/
function buildOptionString() {
let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
// 1. Determine the path: prefer opts.path, fall back to defaultOpts.path, then “/”
const path = opts.path != null ? opts.path : defaultOpts.path != null ? defaultOpts.path : '/';
// 2. Determine domainLevel: prefer opts.domainLevel, fall back to defaultOpts.domainLevel
// (if neither is a number, domainLevel will be undefined)
const domainLevelCandidate = opts.domainLevel != null ? opts.domainLevel : defaultOpts.domainLevel;
const domain = typeof domainLevelCandidate === 'number' ? pickDomain(domainLevelCandidate) : '';
// 3. Determine the “expires” source: prefer opts.expires, fall back to defaultOpts.expires
// (if neither is provided, expireSource will be undefined, and normalizeExpires will treat it as “10 years from now”)
const expiresSource = opts.expires != null ? opts.expires : defaultOpts.expires;
const expiresDate = normalizeExpires(expiresSource);
// 4. Build the string fragments
const expiresPart = `; expires=${expiresDate.toUTCString()}`;
const pathPart = `; path=${path}`;
const domainPart = domain ? `; domain=${domain}` : '';
return `${expiresPart}${pathPart}${domainPart}`;
}
return {
/**
* Retrieve a cookie value by key.
* If the stored value is valid JSON, returns the parsed object;
* otherwise returns the raw string.
*
* @param {string} key - The cookie key to read.
* @returns {any|null} - Parsed object/string or null if not found.
*/
getItem(key) {
const all = document.cookie.split('; ');
const match = all.find(row => row.startsWith(`${key}=`));
if (!match) return null;
const rawValue = decodeURIComponent(match.split('=')[1]);
try {
return JSON.parse(rawValue);
} catch (err) {
return rawValue;
}
},
/**
* Set a persistent cookie with a JSON‐serialized value.
*
* @param {string} key - The cookie key to write.
* @param {any} value - Any JSON‐serializable value (will be stringified).
* @param {Object} [opts] - Optional overrides:
* @param {string} [opts.path] - Cookie path override.
* @param {number} [opts.domainLevel] - Number of hostname segments for `domain=`.
* @param {string|number|Date} [opts.expires] - Expiration override.
*/
setItem(key, value) {
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
const jsonString = JSON.stringify(value);
const encodedValue = encodeURIComponent(jsonString);
const optionString = buildOptionString(opts);
document.cookie = `${key}=${encodedValue}${optionString}`;
},
/**
* Remove a cookie by setting its expiry to the past.
* Must match the same `path` and `domain` used when it was created.
*
* @param {string} key - The cookie key to delete.
* @param {Object} [opts] - Optional overrides:
* @param {string} [opts.path] - Cookie path override.
* @param {number} [opts.domainLevel] - Hostname segments for `domain=` override.
*/
removeItem(key) {
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const past = new Date(0).toUTCString(); // “Thu, 01 Jan 1970 00:00:00 GMT”
// Reuse `buildOptionString` logic but swap in the past date
const override = _objectSpread2(_objectSpread2({}, opts), {}, {
expires: past
});
const optionString = buildOptionString(override);
// Write an empty value and force expiry in the past
document.cookie = `${key}=;${optionString}`;
},
/**
* List all cookie names currently set (for debugging or clearAll).
*
* @returns {string[]} - Array of cookie keys (empty array if no cookies).
*/
keys() {
if (!document.cookie.length) return [];
return document.cookie.split('; ').map(pair => pair.split('=')[0]);
},
/**
* Remove all cookies currently set (respecting factory defaults and any overrides).
* Uses `removeItem` on each key.
*
* @param {Object} [opts] - Optional overrides group(same shape as removeItem opts).
*/
clearAll() {
let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.keys().forEach(keyName => this.removeItem(keyName, opts));
}
};
}
exports.createCookieStorage = createCookieStorage;
Object.defineProperty(exports, '__esModule', { value: true });
});