UNPKG

@localazy/cdn-client

Version:

Node.js module that allows you to easily interact with the Localazy CDN.

419 lines (418 loc) 13.2 kB
/* @localazy/cdn-client@1.5.16 * (c) 2026 Localazy <team@localazy.com> * @license MIT */ (function(e, t) { "object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).LocalazyCDN = {}); })(this, function(e) { Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }); var t = class { context; constructor(e) { this.context = e; } async fetchLocale(e) { return this.context.cache.has(e) ? new Promise((t) => { t(this.context.cache.get(e)); }) : this.context.client.get(e.metafileLocale.uri); } async fetchMetafile() { return await this.context.client.get(this.context.metafile.params.jsonPath); } }, s = class { map; constructor() { this.map = /* @__PURE__ */ new Map(); } get(e) { return this.map.get(e); } has(e) { return this.map.has(e); } set(e, t) { this.map.set(e, t); } flush() { this.map = /* @__PURE__ */ new Map(); } }, a = (e) => "string" == typeof e, i = (e) => void 0 === e, l = (e) => Array.isArray(e), r = (e) => "[object Object]" === Object.prototype.toString.call(e), o = (e) => [...new Set(e)], n = (e, t) => { const s = {}; return e.filter((e) => { const a = t(e); return !Object.hasOwn(s, a) && (s[a] = !0, !0); }); }, c = class { context; cacheAdapter; constructor(e) { this.context = e, this.cacheAdapter = new s(); } setIfMissed(e) { const { metafileFile: t, metafileLocale: s, data: a } = e, i = this.keyFromMetafile({ metafileFile: t, metafileLocale: s }); this.cacheAdapter.has(i) || this.cacheAdapter.set(i, a); } has(e) { const t = this.keyFromMetafile(e); return this.cacheAdapter.has(t); } get(e) { const t = this.keyFromMetafile(e); return this.cacheAdapter.get(t); } flush() { this.cacheAdapter.flush(); } keyFromMetafile(e) { const { metafileFile: t, metafileLocale: s } = e, a = [...o(t.productFlavors)].sort().join("-"); return [ this.context.metafile.params.cdnId, t.id, t.file, t.path, t.library, t.module, t.buildType, a, s.locale, s.timestamp.toString() ].filter((e) => "" !== e).join("-"); } }, h = class e { context; constructor(e) { this.context = e; } createCdnResponse(t) { const { requests: s, responses: a, hasSingleFileResponse: i, hasSingleLocaleResponse: l } = t; return 0 === a.length || void 0 === a[0] ? {} : (this.cacheResponses(s, a), i && l ? a[0] : e.transformResponses(t)); } cacheResponses(e, t) { t.forEach((t, s) => { if (void 0 !== e[s]) { const { metafileFile: a, metafileLocale: i } = e[s]; a && i && this.context.cache.setIfMissed({ metafileFile: a, metafileLocale: i, data: t }); } }); } static transformResponses(e) { const { requests: t, responses: s, hasSingleFileResponse: a } = e; return s.reduce((e, s, i) => { if (void 0 !== t[i]) { const { metafileFile: l, metafileLocale: r } = t[i]; l && r && (a ? e[r.locale] = s : (e[l.id] || (e[l.id] = {}), e[l.id][r.locale] = s)); } return e; }, {}); } }, u = class { metafile; cdn; client; api; cache; responseFactory; constructor(e) { this.metafile = e.metafileContext, this.cdn = e.cdn, this.client = e.client, this.api = new t(this), this.cache = new c(this), this.responseFactory = new h(this); } }, d = class { id; file; path; library; module; buildType; timestamp; productFlavors; locales; baseUrl; constructor(e) { this.id = e.id, this.file = e.file, this.path = e.path, this.library = e.library, this.module = e.module, this.buildType = e.buildType, this.timestamp = e.timestamp, this.productFlavors = e.productFlavors, this.locales = e.locales, this.baseUrl = e.baseUrl; } toCdnFile() { return { id: this.id, file: this.file, path: this.path, library: this.library, module: this.module, buildType: this.buildType, productFlavors: this.productFlavors, locales: this.locales.map((e) => ({ locale: e.locale, isBaseLocale: e.isBaseLocale, uri: `${this.baseUrl}${e.uri}` })) }; } }, f = class { language; region; script; isRtl; name; localizedName; uri; timestamp; baseLocale; constructor(e, t) { this.language = e.language, this.region = e.region, this.script = e.script, this.isRtl = e.isRtl, this.name = e.name, this.localizedName = e.localizedName, this.uri = e.uri, this.timestamp = e.timestamp, this.baseLocale = t; } get locale() { return this.language && this.region && this.script ? `${this.language}_${this.region}#${this.script}` : this.language && this.script ? `${this.language}#${this.script}` : this.language && this.region ? `${this.language}_${this.region}` : this.language; } get isBaseLocale() { return this.locale === this.baseLocale; } toCdnLocale() { return { locale: this.locale, isBaseLocale: this.isBaseLocale, language: this.language, region: this.region, script: this.script, isRtl: this.isRtl, name: this.name, localizedName: this.localizedName }; } }, p = class e { projectUrl; baseLocale; locales; timestamp; files; filesMap; constructor(t, s) { this.projectUrl = t.projectUrl, this.timestamp = t.timestamp, this.files = e.filesFactory(t.files, t.baseLocale, s), this.filesMap = e.filesMapFactory(this.files), this.locales = e.localesFactory(this.files), this.baseLocale = this.locales.find((e) => e.isBaseLocale); } static createEmpty(t) { return new e({ projectUrl: "", baseLocale: "", timestamp: 0, files: {} }, t); } static filesFactory(e, t, s) { return Object.keys(e).reduce((a, i) => { if (void 0 !== e[i]) { const l = e[i].locales.map((e) => new f(e, t)); a.push(new d({ ...e[i], id: i, locales: l, baseUrl: s.baseUrl })); } return a; }, []); } static filesMapFactory(e) { return e.reduce((e, t) => (e[t.id] = t, e), {}); } static localesFactory(e) { return n(e.reduce((e, t) => (e.push(...t.locales.map((e) => e.toCdnLocale())), e), []), (e) => e.locale); } }, m = class e { options; constructor(t) { this.options = e.parseMetafileUrl(t); } get url() { return this.options.url; } get baseUrl() { return this.options.baseUrl; } get cdnId() { return this.options.cdnId; } get jsonPath() { return this.options.jsonPath; } static parseMetafileUrl(e) { let t; try { t = new URL(e); } catch { throw new Error("Invalid param: \"options.metafile\" cannot be parsed as url."); } const s = /^\/(.*?)\/(.*?)\.(v2.json|js|ts)$/.exec(t.pathname); if (null === s || 4 !== s.length || void 0 === s[1] || void 0 === s[2]) throw new Error("Invalid param: \"options.metafile\" contains invalid metafile url."); const a = s[1], i = s[2]; return { url: e, baseUrl: t.origin, cdnId: a, jsonPath: `/${a}/${i}.v2.json` }; } }, g = class { params; parsedData; data; constructor(e) { this.params = new m(e.metafile), this.parsedData = null, this.data = p.createEmpty(this.params); } setMetafile(e) { this.parsedData = e, this.data = new p(e, this.params); } }, w = class { baseUrl; constructor(e) { this.baseUrl = e; } async get(e) { const t = await fetch(`${this.baseUrl}${e}`), s = t.headers.get("content-type"), a = "application/json5" === s || "application/json" === s; if (t.status >= 400) throw new Error(`Request failed with status code ${t.status.toString()}`); let i; return i = a ? await t.json() : await t.text(), i; } }, x = class { context; constructor(e) { this.context = e; } }, y = class extends x { flush = () => { this.context.cache.flush(); }; }, b = class extends x { get projectUrl() { return this.context.metafile.data.projectUrl; } get baseLocale() { return this.context.metafile.data.baseLocale; } get url() { return this.context.metafile.params.url; } get files() { return this.context.metafile.data.files.map((e) => e.toCdnFile()); } locales = (e) => { const { excludeBaseLocale: t } = e || {}, { locales: s } = this.context.metafile.data; return t ? s.filter((e) => !e.isBaseLocale) : s; }; refresh = async () => { const e = await this.context.api.fetchMetafile(); this.context.metafile.setMetafile(e); }; switch = async (e) => { this.context.metafile.params = new m(e.metafile), await this.refresh(); }; }, F = class { data; context; constructor(e) { this.context = e.context, this.data = e.data || {}; } }, L = class { files; localesMap; hasSingleFileResponse; hasSingleLocaleResponse; context; constructor(e) { this.files = [], this.localesMap = new F({ context: e }), this.hasSingleFileResponse = !1, this.hasSingleLocaleResponse = !1, this.context = e; } async execute() { const e = this.getPromises(), t = e.map((e) => e[0]), s = e.map((e) => e[1]), a = await Promise.all(t); return this.context.responseFactory.createCdnResponse({ requests: s, responses: a, localesMap: this.localesMap, hasSingleFileResponse: this.hasSingleFileResponse, hasSingleLocaleResponse: this.hasSingleLocaleResponse }); } getPromises() { return this.files.reduce((e, t) => (void 0 !== this.localesMap.data?.[t.id] && e.push(...this.localesMap.data[t.id].map((e) => { const s = { metafileFile: t, metafileLocale: e }; return [this.context.api.fetchLocale(s), s]; })), e), []); } }, M = class { context; request; constructor(e) { this.context = e, this.request = new L(this.context); } addFiles(e) { if (!(r(e) || a(e) || i(e) || l(e))) throw new Error("Invalid param: \"request.files\" must be object, array, string or undefined."); if (l(e) && e.forEach((e) => { if (!r(e) && !a(e)) throw new Error("Invalid param: array \"request.files\" must contain objects or strings."); }), a(e)) { this.request.hasSingleFileResponse = !0; const t = this.context.metafile.data.files.find((t) => t.id === e); if (!(t instanceof d)) throw new Error(`File not found: "${e}".`); this.request.files = [t]; } else if (i(e)) this.request.files = [...this.context.metafile.data.files]; else if (l(e)) this.request.files = e.map((e) => { let t; if (a(e)) { const s = this.context.metafile.data.files.find((t) => t.id === e); if (i(s)) throw new Error(`File not found: "${e}".`); t = s; } else { const s = this.context.metafile.data.files.find((t) => t.id === e.id); if (i(s)) throw new Error(`File not found: "${e.id}".`); t = s; } return t; }); else if (r(e)) { this.request.hasSingleFileResponse = !0; const t = this.context.metafile.data.files.find((t) => t.id === e.id); if (i(t)) throw new Error(`File not found: "${e.id}".`); this.request.files = [t]; } return this; } addLocales(e, t) { if (!(a(e) || i(e) || l(e))) throw new Error("Invalid param: \"request.locales\" must be array, string or undefined."); return l(e) && e.forEach((e) => { if (!a(e)) throw new Error("Invalid param: array \"request.locales\" must contain strings."); }), a(e) ? (this.request.hasSingleLocaleResponse = !0, this.request.files.reduce((t, s) => (t.data[s.id] = s.locales.filter((t) => t.locale === e), t), this.request.localesMap)) : i(e) ? this.request.files.reduce((e, s) => (e.data[s.id] = t ? s.locales.filter((e) => !e.isBaseLocale) : s.locales, e), this.request.localesMap) : l(e) && this.request.files.reduce((t, s) => (t.data[s.id] = s.locales.filter((t) => e.includes(t.locale)), t), this.request.localesMap), this; } getCdnRequest() { const e = this.request; return this.request = new L(this.context), e; } }; e.Api = t, e.CdnBase = x, e.CdnCache = y, e.CdnClient = class e { metafile; cache; context; static version = "1.5.16"; constructor(e) { const t = new g(e), s = new w(t.params.baseUrl); this.context = new u({ metafileContext: t, cdn: this, client: s }), this.metafile = new b(this.context), this.cache = new y(this.context); } fetch = async (e) => { const { files: t, locales: s, excludeBaseLocale: a } = e || {}; return new M(this.context).addFiles(t).addLocales(s, a).getCdnRequest().execute(); }; static async create(t) { if (!t) throw new Error("Invalid param: missing required \"options\" parameter."); if (!a(t.metafile)) throw new Error("Invalid param: \"options.metafile\" must be string."); const s = new e(t); return await s.metafile.refresh(), s; } }, e.CdnMetafile = b, e.Context = u, e.FetchHttpAdapter = w, e.LocalesCache = c, e.LocalesMap = F, e.MemoryCacheAdapter = s, e.MetafileContext = g, e.MetafileData = p, e.MetafileFile = d, e.MetafileLocale = f, e.MetafileParams = m, e.Request = L, e.RequestBuilder = M, e.ResponseFactory = h, e.isArray = l, e.isPlainObject = r, e.isString = a, e.isUndefined = i, e.uniq = o, e.uniqBy = n; }); //# sourceMappingURL=localazy-cdn-client.umd.min.js.map