langsys-js-typescript 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +268 -0
- package/dist/index.d.mts +428 -0
- package/dist/index.d.ts +428 -0
- package/dist/index.js +1249 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1231 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1249 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/signal.ts
|
|
4
|
+
function createSignal(initial) {
|
|
5
|
+
let value = initial;
|
|
6
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
7
|
+
const subscribe = (run) => {
|
|
8
|
+
subscribers.add(run);
|
|
9
|
+
run(value);
|
|
10
|
+
return () => {
|
|
11
|
+
subscribers.delete(run);
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const set = (next) => {
|
|
15
|
+
if (Object.is(value, next)) return;
|
|
16
|
+
value = next;
|
|
17
|
+
for (const run of subscribers) run(value);
|
|
18
|
+
};
|
|
19
|
+
const update = (fn) => {
|
|
20
|
+
set(fn(value));
|
|
21
|
+
};
|
|
22
|
+
const get = () => value;
|
|
23
|
+
return { subscribe, set, update, get };
|
|
24
|
+
}
|
|
25
|
+
function getValue(signal) {
|
|
26
|
+
return signal.get();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/logger.ts
|
|
30
|
+
var STYLES = {
|
|
31
|
+
log: "display:inline-block;background:rgb(239,239,244);color:black;font-weight:bold;padding:3px 7px;border-radius:3px",
|
|
32
|
+
warn: "display:inline-block;background:gold;color:black;font-weight:bold;padding:3px 7px;border-radius:3px",
|
|
33
|
+
error: "display:inline-block;background:#e0005a;color:#fff;font-weight:bold;padding:3px 7px;border-radius:3px"
|
|
34
|
+
};
|
|
35
|
+
var supportsStyle = typeof window !== "undefined";
|
|
36
|
+
function emit(level, label, args) {
|
|
37
|
+
if (supportsStyle) {
|
|
38
|
+
console[level](`%c${label}`, STYLES[level], ...args);
|
|
39
|
+
} else {
|
|
40
|
+
console[level](`[${label}]`, ...args);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
var Logger = class {
|
|
44
|
+
constructor(debugEnabled = false) {
|
|
45
|
+
this.debugEnabled = debugEnabled;
|
|
46
|
+
}
|
|
47
|
+
log(...args) {
|
|
48
|
+
if (!this.debugEnabled) return;
|
|
49
|
+
emit("log", "Langsys Debug", args);
|
|
50
|
+
}
|
|
51
|
+
warn(...args) {
|
|
52
|
+
emit("warn", "Langsys Warning", args);
|
|
53
|
+
}
|
|
54
|
+
error(...args) {
|
|
55
|
+
emit("error", "Langsys Error", args);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var logger = new Logger(false);
|
|
59
|
+
|
|
60
|
+
// src/api.ts
|
|
61
|
+
var LangsysAppAPIClass = class {
|
|
62
|
+
constructor() {
|
|
63
|
+
this.apiurl = "https://api.langsys.dev/api";
|
|
64
|
+
this.config = {
|
|
65
|
+
projectid: "",
|
|
66
|
+
key: "",
|
|
67
|
+
sUserLocale: createSignal(""),
|
|
68
|
+
baseLocale: "en"
|
|
69
|
+
};
|
|
70
|
+
this.headers = {
|
|
71
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
72
|
+
"x-Authorization": this.config.key
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
setup(config2) {
|
|
76
|
+
if (config2?.key && config2?.projectid) {
|
|
77
|
+
this.config = config2;
|
|
78
|
+
this.headers["x-Authorization"] = this.config.key;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Optional hook to point the client at a different API host. */
|
|
82
|
+
setBaseUrl(url) {
|
|
83
|
+
this.apiurl = url.replace(/\/$/, "");
|
|
84
|
+
}
|
|
85
|
+
async validate(config2) {
|
|
86
|
+
this.setup(config2);
|
|
87
|
+
return await this.get("authorize-project/[projectid]");
|
|
88
|
+
}
|
|
89
|
+
async getTranslations(locale) {
|
|
90
|
+
return await this.get("projects/[projectid]/translations", { locale });
|
|
91
|
+
}
|
|
92
|
+
async post(path, data = {}) {
|
|
93
|
+
return await this.send("POST", path, data);
|
|
94
|
+
}
|
|
95
|
+
async get(path, data = {}) {
|
|
96
|
+
return await this.send("GET", path, data);
|
|
97
|
+
}
|
|
98
|
+
async delete(path, data = {}) {
|
|
99
|
+
return await this.send("DELETE", path, data);
|
|
100
|
+
}
|
|
101
|
+
async patch(path, data = {}) {
|
|
102
|
+
return await this.send("PATCH", path, data);
|
|
103
|
+
}
|
|
104
|
+
async put(path, data = {}) {
|
|
105
|
+
return await this.send("PUT", path, data);
|
|
106
|
+
}
|
|
107
|
+
async send(method, path, data = {}) {
|
|
108
|
+
if (!this.config.projectid || !this.config.key) {
|
|
109
|
+
return {
|
|
110
|
+
status: false,
|
|
111
|
+
errors: ["Missing projectid or API key in configuration"],
|
|
112
|
+
http: {
|
|
113
|
+
status: 0,
|
|
114
|
+
statusText: "Configuration Error",
|
|
115
|
+
url: `${this.apiurl}/${path}`,
|
|
116
|
+
data: JSON.stringify(data)
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (path.startsWith("/")) path = path.substring(1);
|
|
121
|
+
if (path.includes("/[projectid]")) {
|
|
122
|
+
path = path.replaceAll("/[projectid]", `/${this.config.projectid}`);
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const querystring = method === "GET" ? "?" + new URLSearchParams(data).toString() : "";
|
|
126
|
+
const query = await fetch(`${this.apiurl}/${path}${querystring === "?" ? "" : querystring}`, {
|
|
127
|
+
headers: this.headers,
|
|
128
|
+
method,
|
|
129
|
+
body: method === "GET" ? void 0 : JSON.stringify(data)
|
|
130
|
+
});
|
|
131
|
+
const responseData = await query.json();
|
|
132
|
+
const http = {
|
|
133
|
+
status: query.status,
|
|
134
|
+
statusText: query.statusText,
|
|
135
|
+
url: query.url,
|
|
136
|
+
data: JSON.stringify(data)
|
|
137
|
+
};
|
|
138
|
+
responseData.http = http;
|
|
139
|
+
if (!query.ok) {
|
|
140
|
+
logger.warn("LangsysAppAPI failed to query", responseData);
|
|
141
|
+
return {
|
|
142
|
+
status: false,
|
|
143
|
+
errors: [`HTTP ${query.status}: ${query.statusText}`],
|
|
144
|
+
data: responseData,
|
|
145
|
+
http
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return responseData;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
151
|
+
logger.error("LangsysAppAPI Error:", errorMessage);
|
|
152
|
+
logger.error("Request details:", {
|
|
153
|
+
method,
|
|
154
|
+
url: `${this.apiurl}/${path}`,
|
|
155
|
+
data
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
status: false,
|
|
159
|
+
errors: ["Error communicating with API Server", errorMessage],
|
|
160
|
+
http: {
|
|
161
|
+
status: 0,
|
|
162
|
+
statusText: "Network Error",
|
|
163
|
+
url: `${this.apiurl}/${path}`,
|
|
164
|
+
data: JSON.stringify(data)
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var LangsysAppAPI = new LangsysAppAPIClass();
|
|
171
|
+
|
|
172
|
+
// src/persist.ts
|
|
173
|
+
function persist(key, initial) {
|
|
174
|
+
const storage = getSafeStorage();
|
|
175
|
+
let seed = initial;
|
|
176
|
+
if (storage) {
|
|
177
|
+
try {
|
|
178
|
+
const raw = storage.getItem(key);
|
|
179
|
+
if (raw !== null) seed = JSON.parse(raw);
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const signal = createSignal(seed);
|
|
184
|
+
if (storage) {
|
|
185
|
+
signal.subscribe((value) => {
|
|
186
|
+
try {
|
|
187
|
+
storage.setItem(key, JSON.stringify(value));
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return signal;
|
|
193
|
+
}
|
|
194
|
+
function getSafeStorage() {
|
|
195
|
+
if (typeof window === "undefined") return null;
|
|
196
|
+
try {
|
|
197
|
+
const s = window.localStorage;
|
|
198
|
+
s.getItem("__langsys_probe__");
|
|
199
|
+
return s;
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/stores.ts
|
|
206
|
+
var initialTranslations = {
|
|
207
|
+
__uncategorized__: {
|
|
208
|
+
__category__: "__uncategorized__",
|
|
209
|
+
__symbol__: "__uncategorized__"
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var sTranslations = persist("translations", initialTranslations);
|
|
213
|
+
var currentlyLoadedLocale = createSignal("");
|
|
214
|
+
var contentBlocks = createSignal([]);
|
|
215
|
+
var config = {
|
|
216
|
+
projectid: "",
|
|
217
|
+
key: "",
|
|
218
|
+
sUserLocale: createSignal(""),
|
|
219
|
+
baseLocale: "en",
|
|
220
|
+
debug: false,
|
|
221
|
+
key_type: "read"
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/interpolate.ts
|
|
225
|
+
function interpolate(template, params) {
|
|
226
|
+
return template.replace(/\{([^{}]+)\}/g, (match, rawKey) => {
|
|
227
|
+
const key = rawKey.trim();
|
|
228
|
+
if (!(key in params)) return match;
|
|
229
|
+
const value = params[key];
|
|
230
|
+
if (value === void 0 || value === null) return match;
|
|
231
|
+
if (value instanceof Date) return value.toISOString();
|
|
232
|
+
return String(value);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/translations.ts
|
|
237
|
+
function isObject(test) {
|
|
238
|
+
return typeof test === "object" && !Array.isArray(test) && test !== null;
|
|
239
|
+
}
|
|
240
|
+
function sameToken(a, b) {
|
|
241
|
+
return a.projectid === b.projectid && a.category === b.category && a.token === b.token;
|
|
242
|
+
}
|
|
243
|
+
var Translations = class {
|
|
244
|
+
constructor(config2) {
|
|
245
|
+
this.lastLoaded = {};
|
|
246
|
+
this.missingTokens = [];
|
|
247
|
+
this.timer = null;
|
|
248
|
+
this.flushScheduled = false;
|
|
249
|
+
this.isFirstClientRun = true;
|
|
250
|
+
this.config = config2;
|
|
251
|
+
this.debug = new Logger(!!config2.debug);
|
|
252
|
+
this.locale = config2.baseLocale || "";
|
|
253
|
+
this.tSignal = createSignal(this.buildTFn());
|
|
254
|
+
sTranslations.subscribe(() => this.tSignal.set(this.buildTFn()));
|
|
255
|
+
currentlyLoadedLocale.subscribe(() => this.tSignal.set(this.buildTFn()));
|
|
256
|
+
if (config2.key && config2.projectid) this.setup(config2);
|
|
257
|
+
}
|
|
258
|
+
/** Current translation function. Reads fresh state on every call. */
|
|
259
|
+
get t() {
|
|
260
|
+
return this.tSignal.get();
|
|
261
|
+
}
|
|
262
|
+
/** Subscribe to translation snapshot changes. Fires immediately with the current value. */
|
|
263
|
+
subscribe(run) {
|
|
264
|
+
return sTranslations.subscribe(run);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Content-block-keyed lookup used by the DOM `Translate` class.
|
|
268
|
+
* Returns the per-token translation inside a content block, or `null` if
|
|
269
|
+
* either the content block or the inner token is missing. Never registers
|
|
270
|
+
* a missing token — the DOM class manages its own lifecycle via content blocks.
|
|
271
|
+
*/
|
|
272
|
+
lookupContent(category, customId, token) {
|
|
273
|
+
const cats = sTranslations.get();
|
|
274
|
+
const lookupCat = category || "__uncategorized__";
|
|
275
|
+
const contentData = cats[lookupCat]?.[customId];
|
|
276
|
+
if (!contentData || typeof contentData !== "object") return null;
|
|
277
|
+
const value = contentData[token];
|
|
278
|
+
return typeof value === "string" ? value : null;
|
|
279
|
+
}
|
|
280
|
+
buildTFn() {
|
|
281
|
+
return (category, phrase, ...args) => {
|
|
282
|
+
const cats = sTranslations.get();
|
|
283
|
+
const lookupCat = category || "__uncategorized__";
|
|
284
|
+
const value = cats[lookupCat]?.[phrase];
|
|
285
|
+
let translated;
|
|
286
|
+
if (typeof value === "string" && value.length > 0) {
|
|
287
|
+
this.debug.log("TRANSLATION FOUND", [lookupCat, phrase, value]);
|
|
288
|
+
translated = value;
|
|
289
|
+
} else {
|
|
290
|
+
this.missingToken(lookupCat, phrase);
|
|
291
|
+
translated = phrase;
|
|
292
|
+
}
|
|
293
|
+
const params = args[0];
|
|
294
|
+
return params ? interpolate(translated, params) : translated;
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
missingToken(category, token) {
|
|
298
|
+
if (token === void 0 || token === null) {
|
|
299
|
+
return this.debug.warn(`Received undefined or null token for category: ${category}`);
|
|
300
|
+
}
|
|
301
|
+
if (this.config.key_type !== "write") {
|
|
302
|
+
this.debug.log(`Skipping missing token collection (API key is ${this.config.key_type || "unknown"}):`, {
|
|
303
|
+
category,
|
|
304
|
+
token
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (/^[0-9a-f]{32}$/.test(token)) return;
|
|
309
|
+
if (token === "toJSON") {
|
|
310
|
+
this.debug.error(`Received toJSON as token ${category}:${token}`, token);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (token === "") {
|
|
314
|
+
this.debug.warn("Received empty token", token);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const missingToken = { category, token, projectid: this.config.projectid };
|
|
318
|
+
if (!this.missingTokens.some((t2) => sameToken(t2, missingToken))) {
|
|
319
|
+
this.debug.log("MISSING TOKEN LOOKUP FAILED", [missingToken, [...this.missingTokens], { ...sTranslations.get() }]);
|
|
320
|
+
this.missingTokens.push(missingToken);
|
|
321
|
+
this.scheduleTokenFlush();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
scheduleTokenFlush() {
|
|
325
|
+
const strategy = this.config.ssrTokenStrategy || "client";
|
|
326
|
+
const isSSR = typeof window === "undefined";
|
|
327
|
+
if (isSSR) {
|
|
328
|
+
switch (strategy) {
|
|
329
|
+
case "server":
|
|
330
|
+
if (!this.flushScheduled) {
|
|
331
|
+
this.flushScheduled = true;
|
|
332
|
+
queueMicrotask(() => {
|
|
333
|
+
this.updateTokens().then(() => {
|
|
334
|
+
this.flushScheduled = false;
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case "auto":
|
|
340
|
+
if (this.missingTokens.length <= 5 && !this.flushScheduled) {
|
|
341
|
+
this.flushScheduled = true;
|
|
342
|
+
queueMicrotask(() => {
|
|
343
|
+
this.updateTokens().then(() => {
|
|
344
|
+
this.flushScheduled = false;
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
case "client":
|
|
350
|
+
default:
|
|
351
|
+
this.debug.log("SSR: Queuing tokens for client-side transmission");
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
if (!this.flushScheduled && !this.timer) {
|
|
356
|
+
this.flushScheduled = true;
|
|
357
|
+
queueMicrotask(() => {
|
|
358
|
+
this.updateTokens().then(() => {
|
|
359
|
+
this.flushScheduled = false;
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
setup(config2) {
|
|
366
|
+
this.config = config2;
|
|
367
|
+
if (!this.config.projectid || !this.config.key) return;
|
|
368
|
+
if (this.config.debug) this.debug.debugEnabled = this.config.debug;
|
|
369
|
+
if (config2.baseLocale) this.locale = config2.baseLocale;
|
|
370
|
+
this.debug.log("TRANSLATION SETUP INITIATED", this.config);
|
|
371
|
+
if (typeof window !== "undefined") {
|
|
372
|
+
if (this.isFirstClientRun && this.missingTokens.length > 0) {
|
|
373
|
+
this.isFirstClientRun = false;
|
|
374
|
+
const strategy = this.config.ssrTokenStrategy || "client";
|
|
375
|
+
if (strategy === "client" || strategy === "auto" && this.missingTokens.length > 5) {
|
|
376
|
+
this.debug.log(`Post-hydration: Flushing ${this.missingTokens.length} SSR-collected tokens`);
|
|
377
|
+
queueMicrotask(() => this.updateTokens());
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (this.timer) clearInterval(this.timer);
|
|
381
|
+
this.timer = setInterval(() => this.updateTokens(), 3e3);
|
|
382
|
+
this.debug.log("UPDATE TOKEN INTERVAL", this.timer);
|
|
383
|
+
} else {
|
|
384
|
+
const strategy = this.config.ssrTokenStrategy || "client";
|
|
385
|
+
this.debug.log(`SSR environment detected - using '${strategy}' token strategy`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* React to a user-locale change. Fetches translations if we haven't
|
|
390
|
+
* loaded this locale within the last 60s (or if forced).
|
|
391
|
+
*/
|
|
392
|
+
async change(locale, force = false, skipFetch = false) {
|
|
393
|
+
if (!locale) return false;
|
|
394
|
+
if (!this.config.projectid || !this.config.key) return false;
|
|
395
|
+
if (skipFetch) {
|
|
396
|
+
this.debug.log("Using pre-fetched translations for locale", locale);
|
|
397
|
+
this.locale = locale;
|
|
398
|
+
this.lastLoaded[locale] = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
const now = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
402
|
+
if (!force && locale === this.locale && this.lastLoaded[locale] && Math.abs(this.lastLoaded[locale] - now) < 60) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
this.debug.log("Locale change detected!", locale);
|
|
406
|
+
this.locale = locale;
|
|
407
|
+
await this.getTranslations();
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
async updateTokens() {
|
|
411
|
+
if (!this.missingTokens.length) return false;
|
|
412
|
+
if (!this.config.projectid || !this.config.key) return false;
|
|
413
|
+
if (this.config.key_type !== "write") {
|
|
414
|
+
this.debug.log(`Skipping token updates (API key is ${this.config.key_type || "unknown"})`);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (typeof window === "undefined") {
|
|
418
|
+
this.debug.log(`Sending ${this.missingTokens.length} tokens from SSR`);
|
|
419
|
+
}
|
|
420
|
+
const currentData = sTranslations.get();
|
|
421
|
+
this.missingTokens = this.missingTokens.filter((tokenObj) => {
|
|
422
|
+
tokenObj.projectid = this.config.projectid;
|
|
423
|
+
if (tokenObj.category in currentData && tokenObj.token in currentData[tokenObj.category]) {
|
|
424
|
+
this.debug.log("Missing token already exists! Skipping:", {
|
|
425
|
+
category: tokenObj.category,
|
|
426
|
+
token: tokenObj.token,
|
|
427
|
+
existing: currentData[tokenObj.category][tokenObj.token]
|
|
428
|
+
});
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
});
|
|
433
|
+
if (!this.missingTokens.length) return false;
|
|
434
|
+
this.debug.log("CREATE MISSING TOKENS", this.missingTokens);
|
|
435
|
+
try {
|
|
436
|
+
const response = await LangsysAppAPI.post("projects/[projectid]/tokens", {
|
|
437
|
+
tokens: this.missingTokens
|
|
438
|
+
});
|
|
439
|
+
if (!response.status) {
|
|
440
|
+
if (response.errors) {
|
|
441
|
+
this.debug.log("TOKEN UPDATE FAIL", this.missingTokens);
|
|
442
|
+
this.debug.error("Error updating project tokens", response.errors);
|
|
443
|
+
}
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
this.missingTokens.forEach((tokenObj) => {
|
|
447
|
+
if (!currentData[tokenObj.category]) {
|
|
448
|
+
currentData[tokenObj.category] = {
|
|
449
|
+
__category__: tokenObj.category,
|
|
450
|
+
__symbol__: tokenObj.category
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
currentData[tokenObj.category][tokenObj.token] = tokenObj.token;
|
|
454
|
+
currentData[tokenObj.category]["__category__"] = tokenObj.category;
|
|
455
|
+
});
|
|
456
|
+
sTranslations.set({ ...currentData });
|
|
457
|
+
this.missingTokens = [];
|
|
458
|
+
return true;
|
|
459
|
+
} catch (err) {
|
|
460
|
+
this.debug.log("CREATE MISSING TOKEN FAILURE", this.missingTokens);
|
|
461
|
+
this.debug.error("Error updating project tokens", err);
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async getTranslations() {
|
|
466
|
+
if (!LangsysAppAPI.config.projectid && this.config.projectid) {
|
|
467
|
+
LangsysAppAPI.setup(this.config);
|
|
468
|
+
}
|
|
469
|
+
const response = await LangsysAppAPI.getTranslations(this.locale);
|
|
470
|
+
this.debug.log("GET TRANSLATIONS API RESPONSE", response);
|
|
471
|
+
if (response.errors) {
|
|
472
|
+
this.debug.error("Error", response.errors[0]);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const trans = response.data;
|
|
476
|
+
if (!isObject(trans["__uncategorized__"])) {
|
|
477
|
+
trans["__uncategorized__"] = {
|
|
478
|
+
__category__: "__uncategorized__",
|
|
479
|
+
__symbol__: "__uncategorized__"
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
for (const cat of Object.keys(trans)) {
|
|
483
|
+
trans[cat]["__category__"] = cat;
|
|
484
|
+
}
|
|
485
|
+
this.debug.log("GET TRANSLATIONS " + this.locale, trans);
|
|
486
|
+
sTranslations.set(trans);
|
|
487
|
+
setTimeout(() => currentlyLoadedLocale.set(this.locale), 100);
|
|
488
|
+
this.lastLoaded[this.locale] = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/langsys-app.ts
|
|
493
|
+
var LangsysAppClass = class {
|
|
494
|
+
constructor() {
|
|
495
|
+
this.translationsLoadingPromise = Promise.resolve();
|
|
496
|
+
this.locales = {};
|
|
497
|
+
this.countries = [];
|
|
498
|
+
this.countriesLocale = "";
|
|
499
|
+
this.dialCodes = [];
|
|
500
|
+
this.dialCodesLocale = "";
|
|
501
|
+
this.currencies = [];
|
|
502
|
+
this.currenciesLocale = "";
|
|
503
|
+
this.config = config;
|
|
504
|
+
this.debug = new Logger(!!this.config.debug);
|
|
505
|
+
this.Translations = new Translations(this.config);
|
|
506
|
+
}
|
|
507
|
+
/** Current translation function. Reads fresh state on every call. */
|
|
508
|
+
get t() {
|
|
509
|
+
return this.Translations.t;
|
|
510
|
+
}
|
|
511
|
+
async refresh() {
|
|
512
|
+
const locale = this.config.sUserLocale.get();
|
|
513
|
+
this.locales = {};
|
|
514
|
+
return this.Translations.change(locale, true);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Initialize Langsys. Configuration must include at least `projectid`,
|
|
518
|
+
* `key`, and `UserLocaleStore` (any Signal<string>).
|
|
519
|
+
*/
|
|
520
|
+
async init(initConfig) {
|
|
521
|
+
const projectid = initConfig.projectid;
|
|
522
|
+
const key = initConfig.key;
|
|
523
|
+
const UserLocaleStore = initConfig.UserLocaleStore;
|
|
524
|
+
let baseLocale = initConfig.baseLocale || "en";
|
|
525
|
+
const debug = initConfig.debug || false;
|
|
526
|
+
const emulateFailureToLoad = initConfig.emulateFailureToLoad || false;
|
|
527
|
+
const ssrTokenStrategy = initConfig.ssrTokenStrategy || "client";
|
|
528
|
+
const initialTranslations2 = initConfig.initialTranslations;
|
|
529
|
+
const initialTranslationsLocale = initConfig.initialTranslationsLocale;
|
|
530
|
+
this.debug.debugEnabled = debug;
|
|
531
|
+
if (debug && initialTranslations2) {
|
|
532
|
+
this.debug.log("SSR initial translations config:", {
|
|
533
|
+
hasInitialTranslations: !!initialTranslations2,
|
|
534
|
+
initialTranslationsLocale,
|
|
535
|
+
categoriesProvided: initialTranslations2 ? Object.keys(initialTranslations2).length : 0,
|
|
536
|
+
categories: initialTranslations2 ? Object.keys(initialTranslations2) : []
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
if (!projectid) {
|
|
540
|
+
this.debug.error("LangsysApp.init missing projectid in configuration object!");
|
|
541
|
+
return { status: false, errors: ["Missing projectid"] };
|
|
542
|
+
}
|
|
543
|
+
if (!key) {
|
|
544
|
+
this.debug.error("LangsysApp.init missing API key in configuration object!");
|
|
545
|
+
return { status: false, errors: ["Missing API key"] };
|
|
546
|
+
}
|
|
547
|
+
if (!UserLocaleStore?.subscribe) {
|
|
548
|
+
this.debug.error(
|
|
549
|
+
"LangsysApp.init missing UserLocaleStore \u2014 pass a Signal<string> (createSignal('en-us') works if you have nothing of your own)."
|
|
550
|
+
);
|
|
551
|
+
return { status: false, errors: ["Missing UserLocaleStore"] };
|
|
552
|
+
}
|
|
553
|
+
baseLocale = baseLocale.toLowerCase();
|
|
554
|
+
if (!emulateFailureToLoad) {
|
|
555
|
+
this.config = {
|
|
556
|
+
projectid,
|
|
557
|
+
key,
|
|
558
|
+
sUserLocale: UserLocaleStore,
|
|
559
|
+
baseLocale,
|
|
560
|
+
debug,
|
|
561
|
+
ssrTokenStrategy
|
|
562
|
+
};
|
|
563
|
+
Object.assign(config, this.config);
|
|
564
|
+
}
|
|
565
|
+
const validateResponse = await LangsysAppAPI.validate(this.config);
|
|
566
|
+
if (validateResponse.status && validateResponse.data) {
|
|
567
|
+
const authData = validateResponse.data;
|
|
568
|
+
if (authData.key_type) {
|
|
569
|
+
this.config.key_type = authData.key_type;
|
|
570
|
+
config.key_type = authData.key_type;
|
|
571
|
+
this.debug.log("API Key Type:", this.config.key_type);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (initialTranslations2 && initialTranslationsLocale) {
|
|
575
|
+
if (!initialTranslations2["__uncategorized__"]) {
|
|
576
|
+
initialTranslations2["__uncategorized__"] = {
|
|
577
|
+
__category__: "__uncategorized__",
|
|
578
|
+
__symbol__: "__uncategorized__"
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
for (const cat of Object.keys(initialTranslations2)) {
|
|
582
|
+
initialTranslations2[cat]["__category__"] = cat;
|
|
583
|
+
}
|
|
584
|
+
sTranslations.set(initialTranslations2);
|
|
585
|
+
currentlyLoadedLocale.set(initialTranslationsLocale);
|
|
586
|
+
this.debug.log("Populated sTranslations with initial data for locale:", initialTranslationsLocale);
|
|
587
|
+
}
|
|
588
|
+
this.Translations.setup(this.config);
|
|
589
|
+
this.config.sUserLocale.subscribe((locale) => {
|
|
590
|
+
if (!validateResponse.status) return;
|
|
591
|
+
this.getLocalesData(locale);
|
|
592
|
+
this.debug.log("SUBSCRIBING TO USER LOCALE STORE");
|
|
593
|
+
const skipFetch = initialTranslationsLocale === locale && !!initialTranslations2;
|
|
594
|
+
this.translationsLoadingPromise = this.Translations.change(locale, false, skipFetch);
|
|
595
|
+
});
|
|
596
|
+
return validateResponse;
|
|
597
|
+
}
|
|
598
|
+
async getDialCodes(inLocale) {
|
|
599
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
600
|
+
if (this.dialCodes.length && this.dialCodesLocale === locale) return this.dialCodes;
|
|
601
|
+
const route = `countries/dial-codes/${locale}`;
|
|
602
|
+
const response = await LangsysAppAPI.get(route);
|
|
603
|
+
if (response.errors || !response.status) {
|
|
604
|
+
this.debug.error("LangsysApp.getDialCodes failed: " + route);
|
|
605
|
+
return [];
|
|
606
|
+
}
|
|
607
|
+
this.dialCodes = response.data;
|
|
608
|
+
this.dialCodesLocale = locale;
|
|
609
|
+
return this.dialCodes;
|
|
610
|
+
}
|
|
611
|
+
async getCurrencies(inLocale) {
|
|
612
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
613
|
+
if (this.currencies.length && this.currenciesLocale === locale) return this.currencies;
|
|
614
|
+
const route = `currencies/${locale}`;
|
|
615
|
+
const response = await LangsysAppAPI.get(route);
|
|
616
|
+
if (response.errors || !response.status) {
|
|
617
|
+
this.debug.error("LangsysApp.getCurrencies failed: " + route);
|
|
618
|
+
return [];
|
|
619
|
+
}
|
|
620
|
+
this.currencies = response.data;
|
|
621
|
+
this.currenciesLocale = locale;
|
|
622
|
+
return this.currencies;
|
|
623
|
+
}
|
|
624
|
+
async getCountries(inLocale) {
|
|
625
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
626
|
+
if (this.countries.length && this.countriesLocale === locale) return this.countries;
|
|
627
|
+
const route = `countries/${locale}`;
|
|
628
|
+
const response = await LangsysAppAPI.get(route);
|
|
629
|
+
if (response.errors || !response.status) {
|
|
630
|
+
this.debug.error("LangsysApp.getCountries failed: " + route);
|
|
631
|
+
return [];
|
|
632
|
+
}
|
|
633
|
+
this.countries = response.data;
|
|
634
|
+
this.countriesLocale = locale;
|
|
635
|
+
return this.countries;
|
|
636
|
+
}
|
|
637
|
+
async getCountryName(forCountryCode, inLocale) {
|
|
638
|
+
if (!forCountryCode) return "";
|
|
639
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
640
|
+
if (!this.countries.length || this.countriesLocale !== locale) await this.getCountries(inLocale);
|
|
641
|
+
const country = this.countries.find((c) => c.code.toLowerCase() === forCountryCode.toLowerCase())?.label;
|
|
642
|
+
if (!country) this.debug.warn("getCountryName failed to match", forCountryCode);
|
|
643
|
+
return country || forCountryCode;
|
|
644
|
+
}
|
|
645
|
+
async getCurrencyName(forCurrencyCode, inLocale) {
|
|
646
|
+
if (!forCurrencyCode) return "";
|
|
647
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
648
|
+
if (!this.currencies.length || this.currenciesLocale !== locale) await this.getCurrencies(inLocale);
|
|
649
|
+
const currency = this.currencies.find((c) => c.code?.toLowerCase() === forCurrencyCode.toLowerCase())?.name;
|
|
650
|
+
if (!currency) this.debug.warn("getCurrencyName failed to match", forCurrencyCode);
|
|
651
|
+
return currency || forCurrencyCode;
|
|
652
|
+
}
|
|
653
|
+
async getLocalesFormat(format = "", inLocale) {
|
|
654
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
655
|
+
const route = `locales/${locale}` + (format ? `/${format}` : "");
|
|
656
|
+
const response = await LangsysAppAPI.get(route);
|
|
657
|
+
if (response.errors || !response.status) {
|
|
658
|
+
this.debug.error(response.errors, response.status, response);
|
|
659
|
+
this.debug.error("LangsysApp.getLocales failed: " + route);
|
|
660
|
+
return format === "flat" ? [] : format === "data" ? [] : {};
|
|
661
|
+
}
|
|
662
|
+
switch (format) {
|
|
663
|
+
case "flat":
|
|
664
|
+
return response.data;
|
|
665
|
+
case "data":
|
|
666
|
+
return response.data;
|
|
667
|
+
default:
|
|
668
|
+
return response.data;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
async getLocalesFlat(inLocale) {
|
|
672
|
+
return await this.getLocalesFormat("flat", inLocale);
|
|
673
|
+
}
|
|
674
|
+
async getLocalesData(inLocale, forceRefresh = false) {
|
|
675
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
676
|
+
if (this.locales?.[locale]?.length && !forceRefresh) return this.locales[locale];
|
|
677
|
+
this.locales[locale] = await this.getLocalesFormat("data", inLocale);
|
|
678
|
+
return this.locales[locale];
|
|
679
|
+
}
|
|
680
|
+
async getLocales(inLocale) {
|
|
681
|
+
return await this.getLocalesFormat("", inLocale);
|
|
682
|
+
}
|
|
683
|
+
async getLocaleNameWithLookup(forLocale, shortName = false, inLocale) {
|
|
684
|
+
if (!forLocale) return "";
|
|
685
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
686
|
+
await this.getLocalesData(locale);
|
|
687
|
+
return this.getLocaleName(forLocale, shortName, locale);
|
|
688
|
+
}
|
|
689
|
+
getLocaleName(forLocale, shortName = false, inLocale) {
|
|
690
|
+
const locale = inLocale || this.config.sUserLocale.get() || this.config.baseLocale;
|
|
691
|
+
let name = "";
|
|
692
|
+
this.locales[locale]?.every((loc) => {
|
|
693
|
+
if (loc.code === forLocale) {
|
|
694
|
+
name = shortName ? loc.lang_name : loc.locale_name;
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
return true;
|
|
698
|
+
});
|
|
699
|
+
if (!name) this.debug.warn("getLocaleNameWithLookup failed to match", forLocale);
|
|
700
|
+
return name;
|
|
701
|
+
}
|
|
702
|
+
/** @deprecated use `getLocaleNameWithLookup` or `getLocaleName` */
|
|
703
|
+
async getLanguageName(forLocale, shortName = false, inLocale) {
|
|
704
|
+
return this.getLocaleNameWithLookup(forLocale, shortName, inLocale);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Detects the end-user's preferred locale.
|
|
708
|
+
*
|
|
709
|
+
* - In browsers, uses `navigator.languages` with fallback to `navigator.language`.
|
|
710
|
+
* - In SSR environments, parses the `Accept-Language` header.
|
|
711
|
+
*/
|
|
712
|
+
detectPreferredLocale(acceptLanguageHeader, supportedLocales) {
|
|
713
|
+
const userLocales = this.getUserLanguagePreferences(acceptLanguageHeader);
|
|
714
|
+
if (userLocales.length === 0) return false;
|
|
715
|
+
if (supportedLocales && supportedLocales.length > 0) {
|
|
716
|
+
const bestMatch = this.findBestLocaleMatch(userLocales, supportedLocales);
|
|
717
|
+
if (bestMatch) return bestMatch;
|
|
718
|
+
}
|
|
719
|
+
return this.normalizeLocale(userLocales[0]);
|
|
720
|
+
}
|
|
721
|
+
getUserLanguagePreferences(acceptLanguageHeader) {
|
|
722
|
+
if (acceptLanguageHeader) {
|
|
723
|
+
try {
|
|
724
|
+
return this.parseAcceptLanguageHeader(acceptLanguageHeader);
|
|
725
|
+
} catch (error) {
|
|
726
|
+
this.debug.warn("Failed to parse Accept-Language header:", error);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (typeof navigator !== "undefined") {
|
|
730
|
+
if (navigator.languages && navigator.languages.length > 0) {
|
|
731
|
+
return Array.from(navigator.languages);
|
|
732
|
+
}
|
|
733
|
+
if (navigator.language) return [navigator.language];
|
|
734
|
+
}
|
|
735
|
+
return [];
|
|
736
|
+
}
|
|
737
|
+
parseAcceptLanguageHeader(header) {
|
|
738
|
+
if (!header || typeof header !== "string" || header.trim() === "") return [];
|
|
739
|
+
try {
|
|
740
|
+
return header.split(",").map((part) => {
|
|
741
|
+
const trimmed = part.trim();
|
|
742
|
+
if (!trimmed || trimmed === "*") return null;
|
|
743
|
+
const [localePart, qPart] = trimmed.split(";q=");
|
|
744
|
+
const locale = localePart?.trim();
|
|
745
|
+
const q = qPart ? parseFloat(qPart) : 1;
|
|
746
|
+
if (!locale || isNaN(q) || q <= 0 || q > 1) return null;
|
|
747
|
+
return { locale, q };
|
|
748
|
+
}).filter((item) => item !== null).sort((a, b) => b.q - a.q).map((item) => item.locale);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
this.debug.warn("Error parsing Accept-Language header:", error);
|
|
751
|
+
return [];
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
findBestLocaleMatch(userLocales, supportedLocales) {
|
|
755
|
+
if (!userLocales.length || !supportedLocales.length) return null;
|
|
756
|
+
const normalizedSupported = supportedLocales.map((l) => this.normalizeLocale(l));
|
|
757
|
+
for (const userLocale of userLocales) {
|
|
758
|
+
const normalizedUser = this.normalizeLocale(userLocale);
|
|
759
|
+
if (normalizedSupported.includes(normalizedUser)) return normalizedUser;
|
|
760
|
+
}
|
|
761
|
+
for (const userLocale of userLocales) {
|
|
762
|
+
const userLang = this.getLanguageCode(userLocale);
|
|
763
|
+
for (const supported of normalizedSupported) {
|
|
764
|
+
if (this.getLanguageCode(supported) === userLang) return supported;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
normalizeLocale(locale) {
|
|
770
|
+
if (!locale || typeof locale !== "string") return locale;
|
|
771
|
+
const parts = locale.split(/[-_]/);
|
|
772
|
+
if (parts.length === 1) return parts[0].toLowerCase();
|
|
773
|
+
const language = parts[0].toLowerCase();
|
|
774
|
+
const region = parts[1].toUpperCase();
|
|
775
|
+
return `${language}-${region}`;
|
|
776
|
+
}
|
|
777
|
+
getLanguageCode(locale) {
|
|
778
|
+
if (!locale || typeof locale !== "string") return "";
|
|
779
|
+
return locale.split(/[-_]/)[0].toLowerCase();
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
var LangsysApp = new LangsysAppClass();
|
|
783
|
+
|
|
784
|
+
// src/utils.ts
|
|
785
|
+
var isEmpty = (data) => {
|
|
786
|
+
if (typeof data === "number" || typeof data === "boolean") return false;
|
|
787
|
+
if (typeof data === "undefined" || data === null) return true;
|
|
788
|
+
if (typeof data.length !== "undefined") {
|
|
789
|
+
return data.length === 0;
|
|
790
|
+
}
|
|
791
|
+
if (typeof data === "object") return Object.keys(data).length === 0;
|
|
792
|
+
return false;
|
|
793
|
+
};
|
|
794
|
+
function md5(inputString) {
|
|
795
|
+
if (typeof inputString !== "string") inputString = JSON.stringify(inputString);
|
|
796
|
+
const hc = "0123456789abcdef";
|
|
797
|
+
function rh(n) {
|
|
798
|
+
let j, s = "";
|
|
799
|
+
for (j = 0; j <= 3; j++) s += hc.charAt(n >> j * 8 + 4 & 15) + hc.charAt(n >> j * 8 & 15);
|
|
800
|
+
return s;
|
|
801
|
+
}
|
|
802
|
+
function ad(x2, y) {
|
|
803
|
+
const l = (x2 & 65535) + (y & 65535);
|
|
804
|
+
const m = (x2 >> 16) + (y >> 16) + (l >> 16);
|
|
805
|
+
return m << 16 | l & 65535;
|
|
806
|
+
}
|
|
807
|
+
function rl(n, c2) {
|
|
808
|
+
return n << c2 | n >>> 32 - c2;
|
|
809
|
+
}
|
|
810
|
+
function cm(q, a2, b2, x2, s, t2) {
|
|
811
|
+
return ad(rl(ad(ad(a2, q), ad(x2, t2)), s), b2);
|
|
812
|
+
}
|
|
813
|
+
function ff(a2, b2, c2, d2, x2, s, t2) {
|
|
814
|
+
return cm(b2 & c2 | ~b2 & d2, a2, b2, x2, s, t2);
|
|
815
|
+
}
|
|
816
|
+
function gg(a2, b2, c2, d2, x2, s, t2) {
|
|
817
|
+
return cm(b2 & d2 | c2 & ~d2, a2, b2, x2, s, t2);
|
|
818
|
+
}
|
|
819
|
+
function hh(a2, b2, c2, d2, x2, s, t2) {
|
|
820
|
+
return cm(b2 ^ c2 ^ d2, a2, b2, x2, s, t2);
|
|
821
|
+
}
|
|
822
|
+
function ii(a2, b2, c2, d2, x2, s, t2) {
|
|
823
|
+
return cm(c2 ^ (b2 | ~d2), a2, b2, x2, s, t2);
|
|
824
|
+
}
|
|
825
|
+
function sb(x2) {
|
|
826
|
+
let i2;
|
|
827
|
+
const nblk = (x2.length + 8 >> 6) + 1;
|
|
828
|
+
const blks = new Array(nblk * 16);
|
|
829
|
+
for (i2 = 0; i2 < nblk * 16; i2++) blks[i2] = 0;
|
|
830
|
+
for (i2 = 0; i2 < x2.length; i2++) blks[i2 >> 2] |= x2.charCodeAt(i2) << i2 % 4 * 8;
|
|
831
|
+
blks[i2 >> 2] |= 128 << i2 % 4 * 8;
|
|
832
|
+
blks[nblk * 16 - 2] = x2.length * 8;
|
|
833
|
+
return blks;
|
|
834
|
+
}
|
|
835
|
+
let i, x = sb("" + inputString), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, olda, oldb, oldc, oldd;
|
|
836
|
+
for (i = 0; i < x.length; i += 16) {
|
|
837
|
+
olda = a;
|
|
838
|
+
oldb = b;
|
|
839
|
+
oldc = c;
|
|
840
|
+
oldd = d;
|
|
841
|
+
a = ff(a, b, c, d, x[i + 0], 7, -680876936);
|
|
842
|
+
d = ff(d, a, b, c, x[i + 1], 12, -389564586);
|
|
843
|
+
c = ff(c, d, a, b, x[i + 2], 17, 606105819);
|
|
844
|
+
b = ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
|
845
|
+
a = ff(a, b, c, d, x[i + 4], 7, -176418897);
|
|
846
|
+
d = ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
|
847
|
+
c = ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
|
848
|
+
b = ff(b, c, d, a, x[i + 7], 22, -45705983);
|
|
849
|
+
a = ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
|
850
|
+
d = ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
|
851
|
+
c = ff(c, d, a, b, x[i + 10], 17, -42063);
|
|
852
|
+
b = ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
|
853
|
+
a = ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
|
854
|
+
d = ff(d, a, b, c, x[i + 13], 12, -40341101);
|
|
855
|
+
c = ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
|
856
|
+
b = ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
|
857
|
+
a = gg(a, b, c, d, x[i + 1], 5, -165796510);
|
|
858
|
+
d = gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
|
859
|
+
c = gg(c, d, a, b, x[i + 11], 14, 643717713);
|
|
860
|
+
b = gg(b, c, d, a, x[i + 0], 20, -373897302);
|
|
861
|
+
a = gg(a, b, c, d, x[i + 5], 5, -701558691);
|
|
862
|
+
d = gg(d, a, b, c, x[i + 10], 9, 38016083);
|
|
863
|
+
c = gg(c, d, a, b, x[i + 15], 14, -660478335);
|
|
864
|
+
b = gg(b, c, d, a, x[i + 4], 20, -405537848);
|
|
865
|
+
a = gg(a, b, c, d, x[i + 9], 5, 568446438);
|
|
866
|
+
d = gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
|
867
|
+
c = gg(c, d, a, b, x[i + 3], 14, -187363961);
|
|
868
|
+
b = gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
|
869
|
+
a = gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
|
870
|
+
d = gg(d, a, b, c, x[i + 2], 9, -51403784);
|
|
871
|
+
c = gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
|
872
|
+
b = gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
|
873
|
+
a = hh(a, b, c, d, x[i + 5], 4, -378558);
|
|
874
|
+
d = hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
|
875
|
+
c = hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
|
876
|
+
b = hh(b, c, d, a, x[i + 14], 23, -35309556);
|
|
877
|
+
a = hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
|
878
|
+
d = hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
|
879
|
+
c = hh(c, d, a, b, x[i + 7], 16, -155497632);
|
|
880
|
+
b = hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
|
881
|
+
a = hh(a, b, c, d, x[i + 13], 4, 681279174);
|
|
882
|
+
d = hh(d, a, b, c, x[i + 0], 11, -358537222);
|
|
883
|
+
c = hh(c, d, a, b, x[i + 3], 16, -722521979);
|
|
884
|
+
b = hh(b, c, d, a, x[i + 6], 23, 76029189);
|
|
885
|
+
a = hh(a, b, c, d, x[i + 9], 4, -640364487);
|
|
886
|
+
d = hh(d, a, b, c, x[i + 12], 11, -421815835);
|
|
887
|
+
c = hh(c, d, a, b, x[i + 15], 16, 530742520);
|
|
888
|
+
b = hh(b, c, d, a, x[i + 2], 23, -995338651);
|
|
889
|
+
a = ii(a, b, c, d, x[i + 0], 6, -198630844);
|
|
890
|
+
d = ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
|
891
|
+
c = ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
|
892
|
+
b = ii(b, c, d, a, x[i + 5], 21, -57434055);
|
|
893
|
+
a = ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
|
894
|
+
d = ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
|
895
|
+
c = ii(c, d, a, b, x[i + 10], 15, -1051523);
|
|
896
|
+
b = ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
|
897
|
+
a = ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
|
898
|
+
d = ii(d, a, b, c, x[i + 15], 10, -30611744);
|
|
899
|
+
c = ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
|
900
|
+
b = ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
|
901
|
+
a = ii(a, b, c, d, x[i + 4], 6, -145523070);
|
|
902
|
+
d = ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
|
903
|
+
c = ii(c, d, a, b, x[i + 2], 15, 718787259);
|
|
904
|
+
b = ii(b, c, d, a, x[i + 9], 21, -343485551);
|
|
905
|
+
a = ad(a, olda);
|
|
906
|
+
b = ad(b, oldb);
|
|
907
|
+
c = ad(c, oldc);
|
|
908
|
+
d = ad(d, oldd);
|
|
909
|
+
}
|
|
910
|
+
return rh(a) + rh(b) + rh(c) + rh(d);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/translate.ts
|
|
914
|
+
var TRANSLATABLE_ATTRIBUTES = [
|
|
915
|
+
"placeholder",
|
|
916
|
+
"alt",
|
|
917
|
+
"title",
|
|
918
|
+
"aria-label",
|
|
919
|
+
"aria-placeholder",
|
|
920
|
+
"data-error",
|
|
921
|
+
"data-error-message",
|
|
922
|
+
"data-validation-message",
|
|
923
|
+
"data-invalid-message",
|
|
924
|
+
"data-required-message",
|
|
925
|
+
"data-pattern-message"
|
|
926
|
+
];
|
|
927
|
+
var VALUE_TRANSLATABLE_ELEMENTS = ["button"];
|
|
928
|
+
var VALUE_TRANSLATABLE_INPUT_TYPES = ["submit", "button"];
|
|
929
|
+
var SEMANTIC_STYLE_PROPERTIES = [
|
|
930
|
+
"font-size",
|
|
931
|
+
"font-weight",
|
|
932
|
+
"font-style",
|
|
933
|
+
"font-variant",
|
|
934
|
+
"line-height",
|
|
935
|
+
"letter-spacing",
|
|
936
|
+
"word-spacing",
|
|
937
|
+
"text-align",
|
|
938
|
+
"text-decoration",
|
|
939
|
+
"text-transform",
|
|
940
|
+
"text-indent",
|
|
941
|
+
"text-shadow",
|
|
942
|
+
"white-space",
|
|
943
|
+
"-webkit-font-smoothing",
|
|
944
|
+
"color",
|
|
945
|
+
"background-color",
|
|
946
|
+
"background-image",
|
|
947
|
+
"background-position",
|
|
948
|
+
"background-size",
|
|
949
|
+
"background-repeat",
|
|
950
|
+
"border",
|
|
951
|
+
"border-top",
|
|
952
|
+
"border-right",
|
|
953
|
+
"border-bottom",
|
|
954
|
+
"border-left",
|
|
955
|
+
"border-radius",
|
|
956
|
+
"border-color",
|
|
957
|
+
"border-width",
|
|
958
|
+
"border-style",
|
|
959
|
+
"margin",
|
|
960
|
+
"margin-top",
|
|
961
|
+
"margin-right",
|
|
962
|
+
"margin-bottom",
|
|
963
|
+
"margin-left",
|
|
964
|
+
"padding",
|
|
965
|
+
"padding-top",
|
|
966
|
+
"padding-right",
|
|
967
|
+
"padding-bottom",
|
|
968
|
+
"padding-left",
|
|
969
|
+
"box-shadow",
|
|
970
|
+
"opacity",
|
|
971
|
+
"filter",
|
|
972
|
+
"display",
|
|
973
|
+
"visibility",
|
|
974
|
+
"list-style",
|
|
975
|
+
"list-style-type",
|
|
976
|
+
"cursor"
|
|
977
|
+
];
|
|
978
|
+
var Translate = class {
|
|
979
|
+
constructor(element, options = {}) {
|
|
980
|
+
this.contentClone = null;
|
|
981
|
+
this.tokens = [];
|
|
982
|
+
this.parseComplete = false;
|
|
983
|
+
this.isTokenizing = false;
|
|
984
|
+
this.lastTranslatedLocale = "";
|
|
985
|
+
this.unsubscribeLocale = null;
|
|
986
|
+
this.element = element;
|
|
987
|
+
this.options = { ...options };
|
|
988
|
+
this.custom_id = options.custom_id || "";
|
|
989
|
+
void this.tokenizeContent();
|
|
990
|
+
this.unsubscribeLocale = currentlyLoadedLocale.subscribe((locale) => {
|
|
991
|
+
if (locale && this.parseComplete) this.translateUpdate(locale);
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
/** Stop reacting to locale changes. Safe to call multiple times. */
|
|
995
|
+
destroy() {
|
|
996
|
+
if (this.unsubscribeLocale) {
|
|
997
|
+
this.unsubscribeLocale();
|
|
998
|
+
this.unsubscribeLocale = null;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
translateUpdate(currentLocale) {
|
|
1002
|
+
if (!this.element?.innerHTML || !this.parseComplete) return;
|
|
1003
|
+
if (this.lastTranslatedLocale === currentLocale) return;
|
|
1004
|
+
const { category = "" } = this.options;
|
|
1005
|
+
if (this.tokens.length === 1) {
|
|
1006
|
+
this.element.innerText = LangsysApp.Translations.t(category, this.tokens[0]);
|
|
1007
|
+
} else {
|
|
1008
|
+
this.translate(Array.from(this.element.childNodes));
|
|
1009
|
+
this.lastTranslatedLocale = currentLocale;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async tokenizeContent() {
|
|
1013
|
+
if (this.isTokenizing) return false;
|
|
1014
|
+
if (!this.element || !this.element.childNodes.length) {
|
|
1015
|
+
this.parseComplete = true;
|
|
1016
|
+
this.isTokenizing = false;
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
this.isTokenizing = true;
|
|
1020
|
+
this.contentClone = this.element.cloneNode(true);
|
|
1021
|
+
const nodes = Array.from(this.contentClone.childNodes || []);
|
|
1022
|
+
this.tokenize(nodes);
|
|
1023
|
+
const { category = "" } = this.options;
|
|
1024
|
+
if (this.tokens.length === 1) {
|
|
1025
|
+
this.element.innerText = LangsysApp.Translations.t(category, this.tokens[0]);
|
|
1026
|
+
} else {
|
|
1027
|
+
const contentBlock = {
|
|
1028
|
+
custom_id: "",
|
|
1029
|
+
category,
|
|
1030
|
+
label: this.options.label,
|
|
1031
|
+
content: this.contentClone.outerHTML,
|
|
1032
|
+
tokens: this.tokens
|
|
1033
|
+
};
|
|
1034
|
+
await this.handleContentBlock(contentBlock);
|
|
1035
|
+
}
|
|
1036
|
+
this.parseComplete = true;
|
|
1037
|
+
this.isTokenizing = false;
|
|
1038
|
+
return true;
|
|
1039
|
+
}
|
|
1040
|
+
async handleContentBlock(contentBlock) {
|
|
1041
|
+
if (isEmpty(this.custom_id)) this.custom_id = this.generateCustomId(contentBlock);
|
|
1042
|
+
contentBlock.custom_id = this.custom_id;
|
|
1043
|
+
const current = contentBlocks.get();
|
|
1044
|
+
const existing = current.find((b) => b.custom_id === this.custom_id);
|
|
1045
|
+
if (!existing) {
|
|
1046
|
+
if (config.key_type === "write") {
|
|
1047
|
+
LangsysAppAPI.post("projects/[projectid]/content-blocks", contentBlock).then((response) => {
|
|
1048
|
+
if (!response.status) {
|
|
1049
|
+
logger.error("Could not save content block", response.errors);
|
|
1050
|
+
} else {
|
|
1051
|
+
contentBlocks.update((blocks) => [...blocks, contentBlock]);
|
|
1052
|
+
}
|
|
1053
|
+
}).catch((err) => logger.error("Could not save content block", err));
|
|
1054
|
+
} else if (config.debug) {
|
|
1055
|
+
logger.log(`Skipping content block save (API key is ${config.key_type || "unknown"})`);
|
|
1056
|
+
}
|
|
1057
|
+
if (this.tokens.length > 1 && this.element) {
|
|
1058
|
+
this.translate(Array.from(this.element.childNodes));
|
|
1059
|
+
this.lastTranslatedLocale = currentlyLoadedLocale.get();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
tokenize(nodes, indices = []) {
|
|
1064
|
+
nodes.forEach((node, index) => {
|
|
1065
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
1066
|
+
const element = node;
|
|
1067
|
+
if (element.getAttribute("translate") === "no") return;
|
|
1068
|
+
}
|
|
1069
|
+
if (node?.hasChildNodes()) {
|
|
1070
|
+
this.applyStylesToNode(node, [...indices, index]);
|
|
1071
|
+
}
|
|
1072
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
1073
|
+
this.tokenizeAttributes(node);
|
|
1074
|
+
}
|
|
1075
|
+
const contentToken = node.nodeValue?.replace(/\s+/g, " ").trim();
|
|
1076
|
+
if (node?.nodeType === Node.TEXT_NODE && contentToken) {
|
|
1077
|
+
this.tokens.push(contentToken);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (!node?.hasChildNodes()) return;
|
|
1081
|
+
this.tokenize(Array.from(node.childNodes), [...indices, index]);
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
tokenizeAttributes(element) {
|
|
1085
|
+
const tagName = element.tagName.toLowerCase();
|
|
1086
|
+
if (tagName === "img") {
|
|
1087
|
+
const img = element;
|
|
1088
|
+
if (img.src) element.setAttribute("src", img.src);
|
|
1089
|
+
}
|
|
1090
|
+
for (const attr of TRANSLATABLE_ATTRIBUTES) {
|
|
1091
|
+
const value = element.getAttribute(attr)?.trim();
|
|
1092
|
+
if (value) this.tokens.push(value);
|
|
1093
|
+
}
|
|
1094
|
+
if (VALUE_TRANSLATABLE_ELEMENTS.includes(tagName)) {
|
|
1095
|
+
const value = element.getAttribute("value")?.trim();
|
|
1096
|
+
if (value) this.tokens.push(value);
|
|
1097
|
+
}
|
|
1098
|
+
if (tagName === "input") {
|
|
1099
|
+
const inputType = element.getAttribute("type")?.toLowerCase();
|
|
1100
|
+
if (inputType && VALUE_TRANSLATABLE_INPUT_TYPES.includes(inputType)) {
|
|
1101
|
+
const value = element.getAttribute("value")?.trim();
|
|
1102
|
+
if (value) this.tokens.push(value);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (tagName === "select") {
|
|
1106
|
+
const options = element.querySelectorAll("option");
|
|
1107
|
+
options.forEach((option) => {
|
|
1108
|
+
const optionText = option.textContent?.trim();
|
|
1109
|
+
if (optionText) this.tokens.push(optionText);
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
findNode(nodes, indices) {
|
|
1114
|
+
let currentNode = nodes[indices[0]];
|
|
1115
|
+
for (let i = 1; i < indices.length; i++) {
|
|
1116
|
+
if (!currentNode || !currentNode.hasChildNodes()) return void 0;
|
|
1117
|
+
currentNode = Array.from(currentNode.childNodes)[indices[i]];
|
|
1118
|
+
}
|
|
1119
|
+
return currentNode;
|
|
1120
|
+
}
|
|
1121
|
+
translate(nodes) {
|
|
1122
|
+
const currentLocale = currentlyLoadedLocale.get();
|
|
1123
|
+
if (currentLocale === config.baseLocale && (this.lastTranslatedLocale === "" || this.lastTranslatedLocale === config.baseLocale)) {
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
nodes.forEach((node) => {
|
|
1127
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
1128
|
+
const element = node;
|
|
1129
|
+
if (element.getAttribute("translate") === "no") return;
|
|
1130
|
+
this.translateAttributes(node);
|
|
1131
|
+
}
|
|
1132
|
+
if (node?.nodeType !== Node.TEXT_NODE) {
|
|
1133
|
+
if (!node?.hasChildNodes()) return;
|
|
1134
|
+
this.translate(Array.from(node.childNodes));
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (isEmpty(node.nodeValue?.trim())) return;
|
|
1138
|
+
if (isEmpty(node.originalNodeValue)) {
|
|
1139
|
+
node.originalNodeValue = node.nodeValue;
|
|
1140
|
+
}
|
|
1141
|
+
const contentToken = node.originalNodeValue?.replace(/\s+/g, " ").trim();
|
|
1142
|
+
if (!contentToken) return;
|
|
1143
|
+
const translation = this.getTranslation(contentToken);
|
|
1144
|
+
if (translation && contentToken && node.originalNodeValue) {
|
|
1145
|
+
node.nodeValue = node.originalNodeValue.replace(contentToken, translation);
|
|
1146
|
+
} else if (node.originalNodeValue) {
|
|
1147
|
+
node.nodeValue = node.originalNodeValue;
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
getTranslation(token) {
|
|
1152
|
+
if (!token) return null;
|
|
1153
|
+
const { category = "" } = this.options;
|
|
1154
|
+
return LangsysApp.Translations.lookupContent(category, this.custom_id, token);
|
|
1155
|
+
}
|
|
1156
|
+
translateAttributes(element) {
|
|
1157
|
+
const tagName = element.tagName.toLowerCase();
|
|
1158
|
+
if (!element.originalAttributes) element.originalAttributes = {};
|
|
1159
|
+
for (const attr of TRANSLATABLE_ATTRIBUTES) {
|
|
1160
|
+
this.translateAttribute(element, attr);
|
|
1161
|
+
}
|
|
1162
|
+
if (VALUE_TRANSLATABLE_ELEMENTS.includes(tagName)) {
|
|
1163
|
+
this.translateAttribute(element, "value");
|
|
1164
|
+
}
|
|
1165
|
+
if (tagName === "input") {
|
|
1166
|
+
const inputType = element.getAttribute("type")?.toLowerCase();
|
|
1167
|
+
if (inputType && VALUE_TRANSLATABLE_INPUT_TYPES.includes(inputType)) {
|
|
1168
|
+
this.translateAttribute(element, "value");
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (tagName === "select") {
|
|
1172
|
+
const options = element.querySelectorAll("option");
|
|
1173
|
+
options.forEach((option) => {
|
|
1174
|
+
const optionEl = option;
|
|
1175
|
+
if (!optionEl.originalAttributes) optionEl.originalAttributes = {};
|
|
1176
|
+
if (optionEl.originalAttributes["textContent"] === void 0) {
|
|
1177
|
+
optionEl.originalAttributes["textContent"] = option.textContent || "";
|
|
1178
|
+
}
|
|
1179
|
+
const originalText = optionEl.originalAttributes["textContent"].trim();
|
|
1180
|
+
if (originalText) {
|
|
1181
|
+
const translation = this.getTranslation(originalText);
|
|
1182
|
+
option.textContent = translation || optionEl.originalAttributes["textContent"];
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
translateAttribute(element, attr) {
|
|
1188
|
+
const currentValue = element.getAttribute(attr);
|
|
1189
|
+
if (!currentValue) return;
|
|
1190
|
+
if (element.originalAttributes[attr] === void 0) {
|
|
1191
|
+
element.originalAttributes[attr] = currentValue;
|
|
1192
|
+
}
|
|
1193
|
+
const originalValue = element.originalAttributes[attr].trim();
|
|
1194
|
+
if (!originalValue) return;
|
|
1195
|
+
const translation = this.getTranslation(originalValue);
|
|
1196
|
+
if (translation) element.setAttribute(attr, translation);
|
|
1197
|
+
else element.setAttribute(attr, element.originalAttributes[attr]);
|
|
1198
|
+
}
|
|
1199
|
+
generateCustomId(contentBlock) {
|
|
1200
|
+
return md5(`${contentBlock.category}-${contentBlock.tokens.join("-")}`);
|
|
1201
|
+
}
|
|
1202
|
+
applyStylesToNode(node, indices) {
|
|
1203
|
+
if (!this.element || typeof window === "undefined") return;
|
|
1204
|
+
const domNode = this.findNode(Array.from(this.element.childNodes), indices);
|
|
1205
|
+
if (!domNode) return;
|
|
1206
|
+
const tagName = domNode.tagName.toLowerCase();
|
|
1207
|
+
const reference = document.createElement(tagName);
|
|
1208
|
+
reference.style.visibility = "hidden";
|
|
1209
|
+
reference.style.position = "absolute";
|
|
1210
|
+
document.body.appendChild(reference);
|
|
1211
|
+
const computed = window.getComputedStyle(domNode);
|
|
1212
|
+
const defaults = window.getComputedStyle(reference);
|
|
1213
|
+
for (const prop of SEMANTIC_STYLE_PROPERTIES) {
|
|
1214
|
+
const value = computed.getPropertyValue(prop);
|
|
1215
|
+
const defaultValue = defaults.getPropertyValue(prop);
|
|
1216
|
+
if (value && value !== defaultValue) {
|
|
1217
|
+
node.style.setProperty(prop, value);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
document.body.removeChild(reference);
|
|
1221
|
+
node.removeAttribute("class");
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
// src/index.ts
|
|
1226
|
+
var t = ((category, phrase, ...args) => {
|
|
1227
|
+
return LangsysApp.Translations.t(category, phrase, ...args);
|
|
1228
|
+
});
|
|
1229
|
+
var tSignal = LangsysApp.Translations.tSignal;
|
|
1230
|
+
|
|
1231
|
+
exports.LangsysApp = LangsysApp;
|
|
1232
|
+
exports.LangsysAppAPI = LangsysAppAPI;
|
|
1233
|
+
exports.Logger = Logger;
|
|
1234
|
+
exports.Translate = Translate;
|
|
1235
|
+
exports.Translations = Translations;
|
|
1236
|
+
exports.contentBlocks = contentBlocks;
|
|
1237
|
+
exports.createSignal = createSignal;
|
|
1238
|
+
exports.currentlyLoadedLocale = currentlyLoadedLocale;
|
|
1239
|
+
exports.getValue = getValue;
|
|
1240
|
+
exports.interpolate = interpolate;
|
|
1241
|
+
exports.isEmpty = isEmpty;
|
|
1242
|
+
exports.logger = logger;
|
|
1243
|
+
exports.md5 = md5;
|
|
1244
|
+
exports.persist = persist;
|
|
1245
|
+
exports.sTranslations = sTranslations;
|
|
1246
|
+
exports.t = t;
|
|
1247
|
+
exports.tSignal = tSignal;
|
|
1248
|
+
//# sourceMappingURL=index.js.map
|
|
1249
|
+
//# sourceMappingURL=index.js.map
|