@localess/cli 0.0.1-dev.20260216094809

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/README.md ADDED
@@ -0,0 +1,62 @@
1
+ <br/>
2
+ <br/>
3
+ <img src="https://github.com/Lessify/localess/wiki/img/logo-adaptive.svg" alt="logo">
4
+ <br/>
5
+ <br/>
6
+
7
+ ----
8
+
9
+ # Localess Command Line
10
+
11
+ This client SDK is designed to work with the Localess API. It provides a simple way to interact with the Localess API from your JavaScript or TypeScript application.
12
+
13
+ > **Important:**
14
+ > The Client is designed to be used on the server side only, as it requires your **Localess API Token** to be kept secret.
15
+ > Do not use this client in your frontend application, as it exposes your API Token to the public.
16
+
17
+ ## Installation
18
+
19
+ ### NPM
20
+ ````bash
21
+ npm install @localess/js-client@latest
22
+ ````
23
+
24
+ ### Yarn
25
+ ````bash
26
+ yarn add @localess/js-client@latest
27
+ ````
28
+
29
+ ## Client
30
+
31
+ ````ts
32
+ import {localessClient} from "@localess/js-client";
33
+
34
+ const llClient = localessClient({
35
+ // A fully qualified domain name with protocol (http/https) and port.
36
+ origin: 'https://my-localess.web.app',
37
+ // Localess space ID, cna be found in the Localess Space settings
38
+ spaceId: 'I1LoVe2LocaLess4Rever',
39
+ // Localess API token, can be found in the Localess Space settings
40
+ token: 'Baz00KaT0KeN8S3CureLL'
41
+ });
42
+
43
+ // Fetch all Content Links
44
+ llClient.getLinks()
45
+ // Fetch content by SLUG
46
+ llClient.getContentBySlug('docs/overview')
47
+ // Fetch content by ID
48
+ llClient.getContentById('FRnIT7CUABoRCdSVVGGs')
49
+ // Fetch translations by locale
50
+ llClient.getTranslations('en')
51
+ ````
52
+
53
+ ## Sync with Visual Editor
54
+
55
+ It will automatically inject Localess Sync Script in to the HTML page.
56
+
57
+ ````ts
58
+ import {loadLocalessSync} from "@localess/js-client";
59
+
60
+ // A fully qualified domain name with protocol (http/https) and port.
61
+ loadLocalessSync('https://my-localess.web.app')
62
+ ````
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,517 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/program.ts
27
+ var import_commander4 = require("commander");
28
+
29
+ // src/commands/login/index.ts
30
+ var import_commander = require("commander");
31
+
32
+ // src/utils.ts
33
+ var RESET = "\x1B[0m";
34
+ var FG_BLUE = "\x1B[34m";
35
+
36
+ // src/cache.ts
37
+ var NoCache = class {
38
+ set(key, value) {
39
+ }
40
+ get(key) {
41
+ return void 0;
42
+ }
43
+ has(key) {
44
+ return false;
45
+ }
46
+ };
47
+ var TTLCache = class {
48
+ /**
49
+ * Creates a TTLCache with a specified time-to-live (TTL) for each entry.
50
+ * default is 5 minutes (300000 ms).
51
+ * @param ttlMs
52
+ */
53
+ constructor(ttlMs = 3e5) {
54
+ this.ttlMs = ttlMs;
55
+ }
56
+ cache = /* @__PURE__ */ new Map();
57
+ set(key, value) {
58
+ this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });
59
+ }
60
+ get(key) {
61
+ const entry = this.cache.get(key);
62
+ if (!entry) return void 0;
63
+ if (Date.now() > entry.expiresAt) {
64
+ this.cache.delete(key);
65
+ return void 0;
66
+ }
67
+ return entry.value;
68
+ }
69
+ has(key) {
70
+ return this.get(key) !== void 0;
71
+ }
72
+ };
73
+
74
+ // src/client.ts
75
+ var LOG_GROUP = `${FG_BLUE}[Localess:Client]${RESET}`;
76
+ function localessClient(options) {
77
+ if (options.debug) {
78
+ console.log(LOG_GROUP, "Client Options : ", options);
79
+ }
80
+ const fetchOptions = {
81
+ redirect: "follow",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ "Accept": "application/json",
85
+ "X-Localess-Agent": "Localess-CLI-Client",
86
+ "X-Localess-Agent-Version": "0.9.0"
87
+ }
88
+ };
89
+ const cache = options.cacheTTL === false ? new NoCache() : new TTLCache(options.cacheTTL);
90
+ return {
91
+ async getSpace() {
92
+ if (options.debug) {
93
+ console.log(LOG_GROUP, "getSpace()");
94
+ }
95
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
96
+ if (options.debug) {
97
+ console.log(LOG_GROUP, "getSpace fetch url : ", url);
98
+ }
99
+ if (cache.has(url)) {
100
+ if (options.debug) {
101
+ console.log(LOG_GROUP, "getSpace cache hit");
102
+ }
103
+ return cache.get(url);
104
+ }
105
+ try {
106
+ const response = await fetch(url, fetchOptions);
107
+ if (options.debug) {
108
+ console.log(LOG_GROUP, "getSpace status : ", response.status);
109
+ }
110
+ const data = await response.json();
111
+ cache.set(url, data);
112
+ return data;
113
+ } catch (error) {
114
+ console.error(LOG_GROUP, "getSpace error : ", error);
115
+ return {};
116
+ }
117
+ },
118
+ async getLinks(params) {
119
+ if (options.debug) {
120
+ console.log(LOG_GROUP, "getLinks() params : ", JSON.stringify(params));
121
+ }
122
+ let kind = "";
123
+ if (params?.kind) {
124
+ kind = `&kind=${params.kind}`;
125
+ }
126
+ let parentSlug = "";
127
+ if (params?.parentSlug) {
128
+ parentSlug = `&parentSlug=${params.parentSlug}`;
129
+ }
130
+ let excludeChildren = "";
131
+ if (params?.excludeChildren) {
132
+ excludeChildren = `&excludeChildren=${params.excludeChildren}`;
133
+ }
134
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
135
+ if (options.debug) {
136
+ console.log(LOG_GROUP, "getLinks fetch url : ", url);
137
+ }
138
+ if (cache.has(url)) {
139
+ if (options.debug) {
140
+ console.log(LOG_GROUP, "getLinks cache hit");
141
+ }
142
+ return cache.get(url);
143
+ }
144
+ try {
145
+ const response = await fetch(url, fetchOptions);
146
+ if (options.debug) {
147
+ console.log(LOG_GROUP, "getLinks status : ", response.status);
148
+ }
149
+ const data = await response.json();
150
+ cache.set(url, data);
151
+ return data;
152
+ } catch (error) {
153
+ console.error(LOG_GROUP, "getLinks error : ", error);
154
+ return {};
155
+ }
156
+ },
157
+ async getContentBySlug(slug, params) {
158
+ if (options.debug) {
159
+ console.log(LOG_GROUP, "getContentBySlug() slug : ", slug);
160
+ console.log(LOG_GROUP, "getContentBySlug() params : ", JSON.stringify(params));
161
+ }
162
+ let version = "";
163
+ if (options?.version && options.version == "draft") {
164
+ version = `&version=${options.version}`;
165
+ }
166
+ if (params?.version && params.version == "draft") {
167
+ version = `&version=${params.version}`;
168
+ }
169
+ const locale = params?.locale ? `&locale=${params.locale}` : "";
170
+ const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
171
+ const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
172
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
173
+ if (options.debug) {
174
+ console.log(LOG_GROUP, "getContentBySlug fetch url : ", url);
175
+ }
176
+ if (cache.has(url)) {
177
+ if (options.debug) {
178
+ console.log(LOG_GROUP, "getContentBySlug cache hit");
179
+ }
180
+ return cache.get(url);
181
+ }
182
+ try {
183
+ const response = await fetch(url, fetchOptions);
184
+ if (options.debug) {
185
+ console.log(LOG_GROUP, "getContentBySlug status : ", response.status);
186
+ }
187
+ const data = await response.json();
188
+ cache.set(url, data);
189
+ return data;
190
+ } catch (error) {
191
+ console.error(LOG_GROUP, "getContentBySlug error : ", error);
192
+ return {};
193
+ }
194
+ },
195
+ async getContentById(id, params) {
196
+ if (options.debug) {
197
+ console.log(LOG_GROUP, "getContentById() id : ", id);
198
+ console.log(LOG_GROUP, "getContentById() params : ", JSON.stringify(params));
199
+ }
200
+ let version = "";
201
+ if (options?.version && options.version == "draft") {
202
+ version = `&version=${options.version}`;
203
+ }
204
+ if (params?.version && params.version == "draft") {
205
+ version = `&version=${params.version}`;
206
+ }
207
+ const locale = params?.locale ? `&locale=${params.locale}` : "";
208
+ const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
209
+ const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
210
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
211
+ if (options.debug) {
212
+ console.log(LOG_GROUP, "getContentById fetch url : ", url);
213
+ }
214
+ if (cache.has(url)) {
215
+ if (options.debug) {
216
+ console.log(LOG_GROUP, "getContentById cache hit");
217
+ }
218
+ return cache.get(url);
219
+ }
220
+ try {
221
+ const response = await fetch(url, fetchOptions);
222
+ if (options.debug) {
223
+ console.log(LOG_GROUP, "getContentById status : ", response.status);
224
+ }
225
+ const data = await response.json();
226
+ cache.set(url, data);
227
+ return data;
228
+ } catch (error) {
229
+ console.error(LOG_GROUP, "getContentById error : ", error);
230
+ return {};
231
+ }
232
+ },
233
+ async getTranslations(locale) {
234
+ if (options.debug) {
235
+ console.log(LOG_GROUP, "getTranslations() locale : ", locale);
236
+ }
237
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}`;
238
+ if (options.debug) {
239
+ console.log(LOG_GROUP, "getTranslations fetch url : ", url);
240
+ }
241
+ if (cache.has(url)) {
242
+ if (options.debug) {
243
+ console.log(LOG_GROUP, "getTranslations cache hit");
244
+ }
245
+ return cache.get(url);
246
+ }
247
+ try {
248
+ const response = await fetch(url, fetchOptions);
249
+ if (options.debug) {
250
+ console.log(LOG_GROUP, "getTranslations status : ", response.status);
251
+ }
252
+ const data = await response.json();
253
+ cache.set(url, data);
254
+ return data;
255
+ } catch (error) {
256
+ console.error(LOG_GROUP, "getTranslations error : ", error);
257
+ return {};
258
+ }
259
+ },
260
+ async updateTranslations(locale, type, values) {
261
+ if (options.debug) {
262
+ console.log(LOG_GROUP, "updateTranslations() locale : ", locale);
263
+ console.log(LOG_GROUP, "updateTranslations() type : ", type);
264
+ console.log(LOG_GROUP, "updateTranslations() values : ", JSON.stringify(values));
265
+ }
266
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
267
+ if (options.debug) {
268
+ console.log(LOG_GROUP, "updateTranslations fetch url : ", url);
269
+ }
270
+ const body = {
271
+ type,
272
+ values
273
+ };
274
+ try {
275
+ const response = await fetch(url, {
276
+ ...fetchOptions,
277
+ method: "POST",
278
+ headers: {
279
+ "X-API-KEY": options.token
280
+ },
281
+ body: JSON.stringify(body)
282
+ });
283
+ if (options.debug) {
284
+ console.log(LOG_GROUP, "updateTranslations status : ", response.status);
285
+ }
286
+ } catch (error) {
287
+ console.error(LOG_GROUP, "updateTranslations error : ", error);
288
+ }
289
+ },
290
+ async getOpenApi() {
291
+ if (options.debug) {
292
+ console.log(LOG_GROUP, "getOpenApi()");
293
+ }
294
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
295
+ if (options.debug) {
296
+ console.log(LOG_GROUP, "getOpenApi fetch url : ", url);
297
+ }
298
+ if (cache.has(url)) {
299
+ if (options.debug) {
300
+ console.log(LOG_GROUP, "getTranslations cache hit");
301
+ }
302
+ return cache.get(url);
303
+ }
304
+ try {
305
+ const response = await fetch(url, fetchOptions);
306
+ if (options.debug) {
307
+ console.log(LOG_GROUP, "getOpenApi status : ", response.status);
308
+ }
309
+ const data = await response.json();
310
+ cache.set(url, data);
311
+ return data;
312
+ } catch (error) {
313
+ console.error(LOG_GROUP, "getOpenApi error : ", error);
314
+ return {};
315
+ }
316
+ },
317
+ syncScriptUrl() {
318
+ return `${options.origin}/scripts/sync-v1.js`;
319
+ },
320
+ assetLink(asset) {
321
+ if (typeof asset === "string") {
322
+ return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
323
+ } else {
324
+ return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
325
+ }
326
+ }
327
+ };
328
+ }
329
+
330
+ // src/session.ts
331
+ var import_promises2 = require("fs/promises");
332
+ var import_node_path2 = require("path");
333
+ var process2 = __toESM(require("process"));
334
+
335
+ // src/file.ts
336
+ var import_promises = require("fs/promises");
337
+ var import_node_path = require("path");
338
+ var DEFAULT_CONFIG_DIR = ".localess";
339
+ async function writeToFile(filePath, data, option) {
340
+ const resolvedPath = (0, import_node_path.parse)(filePath).dir;
341
+ try {
342
+ await (0, import_promises.mkdir)(resolvedPath, { recursive: true });
343
+ } catch (mkdirError) {
344
+ return;
345
+ }
346
+ try {
347
+ await (0, import_promises.writeFile)(filePath, data, option);
348
+ } catch (writeError) {
349
+ }
350
+ }
351
+
352
+ // src/session.ts
353
+ var CREDENTIALS_PATH = (0, import_node_path2.join)(process2.cwd(), DEFAULT_CONFIG_DIR, "credentials.json");
354
+ async function getSession() {
355
+ let session = {
356
+ isLoggedIn: false
357
+ };
358
+ const token = process2.env.LOCALESS_TOKEN;
359
+ const space = process2.env.LOCALESS_SPACE;
360
+ const origin = process2.env.LOCALESS_ORIGIN;
361
+ if (token && space && origin) {
362
+ console.debug(`Login in using environment variables.`);
363
+ return {
364
+ isLoggedIn: true,
365
+ space,
366
+ origin,
367
+ token,
368
+ method: "env"
369
+ };
370
+ }
371
+ try {
372
+ await (0, import_promises2.access)(CREDENTIALS_PATH);
373
+ const content = await (0, import_promises2.readFile)(CREDENTIALS_PATH, "utf8");
374
+ const parsedContent = JSON.parse(content);
375
+ if (Object.keys(parsedContent).length === 0) {
376
+ return session;
377
+ }
378
+ if (parsedContent.origin && parsedContent.token && parsedContent.space) {
379
+ console.debug(`Login in using credentials file.`);
380
+ return {
381
+ isLoggedIn: true,
382
+ space: parsedContent.space,
383
+ origin: parsedContent.origin,
384
+ token: parsedContent.token,
385
+ method: "file"
386
+ };
387
+ }
388
+ } catch (e) {
389
+ console.error('No credentials found. Please log in using the "localess login" command.');
390
+ }
391
+ console.debug("Not logged in.");
392
+ return session;
393
+ }
394
+ async function persistSession(data) {
395
+ if (data.origin && data.token && data.space) {
396
+ await writeToFile(CREDENTIALS_PATH, JSON.stringify(data, null, 2), { mode: 384 });
397
+ console.log("Add session credentials to file system.");
398
+ console.log("Add .localess to .gitignore to avoid committing them to your repository.");
399
+ } else {
400
+ throw new Error("Cannot persist session: missing required fields.");
401
+ }
402
+ }
403
+ async function clearSession() {
404
+ try {
405
+ await (0, import_promises2.access)(CREDENTIALS_PATH);
406
+ await writeToFile(CREDENTIALS_PATH, "{}", { mode: 384 });
407
+ } catch (error) {
408
+ throw new Error("Failed to clear session credentials.");
409
+ }
410
+ }
411
+
412
+ // src/commands/login/index.ts
413
+ var loginCommand = new import_commander.Command("login").description("Login to Localess CLI").option("-t, --token <token>", "Token to login to Localess CLI").option("-s, --space <space>", "Space ID to login to").option("-o, --origin <origin>", "Origin of the Localess instance").action(async (options) => {
414
+ console.log("Logging in with options:", options);
415
+ const session = await getSession();
416
+ if (session.isLoggedIn && session.method === "file") {
417
+ console.log('Already logged in. If you want to log in with different credentials, please log out first using "localess logout" command.');
418
+ return;
419
+ }
420
+ if (options.origin && options.space && options.token) {
421
+ const client = localessClient({
422
+ origin: options.origin,
423
+ spaceId: options.space,
424
+ token: options.token
425
+ });
426
+ try {
427
+ const space = await client.getSpace();
428
+ console.log(`Successfully logged in to space: ${space.name} (${space.id})`);
429
+ await persistSession({
430
+ origin: options.origin,
431
+ space: options.space,
432
+ token: options.token
433
+ });
434
+ } catch (e) {
435
+ console.error("Login failed");
436
+ }
437
+ } else if (session.isLoggedIn && session.method === "env") {
438
+ console.log("Already logged in with environment variables.");
439
+ console.log("If you want to log in with different credentials, Please provide all required options: --origin, --space, and --token");
440
+ } else {
441
+ console.log("Please provide all required options: --origin, --space, and --token");
442
+ console.log("Or set the following environment variables: LOCALESS_ORIGIN, LOCALESS_SPACE, and LOCALESS_TOKEN");
443
+ }
444
+ });
445
+
446
+ // src/commands/logout/index.ts
447
+ var import_commander2 = require("commander");
448
+ var logoutCommand = new import_commander2.Command("logout").description("Logout from Localess CLI").action(async () => {
449
+ console.log("Logging out...");
450
+ const session = await getSession();
451
+ if (!session.isLoggedIn) {
452
+ console.log("Not currently logged in.");
453
+ return;
454
+ }
455
+ if (session.method === "env") {
456
+ console.log("You are logged in using environment variables. To log out, unset LOCALESS_TOKEN, LOCALESS_SPACE, and LOCALESS_ORIGIN.");
457
+ return;
458
+ }
459
+ try {
460
+ await clearSession();
461
+ console.log("Successfully logged out.");
462
+ } catch (e) {
463
+ console.error("Failed to log out:", e);
464
+ }
465
+ });
466
+
467
+ // src/commands/types/index.ts
468
+ var import_commander3 = require("commander");
469
+ var import_openapi_typescript = __toESM(require("openapi-typescript"));
470
+ var import_node_path3 = require("path");
471
+ var import_node_process = __toESM(require("process"));
472
+ var TYPES_PATH = (0, import_node_path3.join)(import_node_process.default.cwd(), DEFAULT_CONFIG_DIR, "localess.d.ts");
473
+ var typesCommand = new import_commander3.Command("types").description("Generate types for your schemas").action(async (options) => {
474
+ console.log("Types in with options:", options);
475
+ const session = await getSession();
476
+ if (!session.isLoggedIn) {
477
+ console.error("Not logged in");
478
+ console.error('Please log in first using "localess login" command');
479
+ return;
480
+ }
481
+ const client = localessClient({
482
+ origin: session.origin,
483
+ spaceId: session.space,
484
+ token: session.token
485
+ });
486
+ console.log("Fetching OpenAPI specification from Localess...");
487
+ const specification = await client.getOpenApi();
488
+ console.log("Generating types from OpenAPI specification...");
489
+ try {
490
+ const minimalSpec = {
491
+ openapi: "3.0.0",
492
+ info: { title: "Schemas Only", version: "1.0.0" },
493
+ components: { schemas: specification.components?.schemas || {} }
494
+ };
495
+ const ast = await (0, import_openapi_typescript.default)(minimalSpec, { exportType: true, rootTypes: true, rootTypesNoSchemaPrefix: true });
496
+ const contents = (0, import_openapi_typescript.astToString)(ast);
497
+ await writeToFile(TYPES_PATH, contents);
498
+ console.log(`Types generated successfully at ${TYPES_PATH}`);
499
+ } catch (e) {
500
+ console.error(e);
501
+ }
502
+ });
503
+
504
+ // src/program.ts
505
+ var program = new import_commander4.Command();
506
+ program.name("Localess CLI").description("CLI tool for Localess platform management").version("0.0.1");
507
+ program.addCommand(loginCommand);
508
+ program.addCommand(logoutCommand);
509
+ program.addCommand(typesCommand);
510
+
511
+ // src/index.ts
512
+ try {
513
+ program.parse(process.argv);
514
+ } catch (e) {
515
+ console.error("Error executing command:", e instanceof Error ? e.message : e);
516
+ process.exit(1);
517
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,494 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/program.ts
4
+ import { Command as Command4 } from "commander";
5
+
6
+ // src/commands/login/index.ts
7
+ import { Command } from "commander";
8
+
9
+ // src/utils.ts
10
+ var RESET = "\x1B[0m";
11
+ var FG_BLUE = "\x1B[34m";
12
+
13
+ // src/cache.ts
14
+ var NoCache = class {
15
+ set(key, value) {
16
+ }
17
+ get(key) {
18
+ return void 0;
19
+ }
20
+ has(key) {
21
+ return false;
22
+ }
23
+ };
24
+ var TTLCache = class {
25
+ /**
26
+ * Creates a TTLCache with a specified time-to-live (TTL) for each entry.
27
+ * default is 5 minutes (300000 ms).
28
+ * @param ttlMs
29
+ */
30
+ constructor(ttlMs = 3e5) {
31
+ this.ttlMs = ttlMs;
32
+ }
33
+ cache = /* @__PURE__ */ new Map();
34
+ set(key, value) {
35
+ this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });
36
+ }
37
+ get(key) {
38
+ const entry = this.cache.get(key);
39
+ if (!entry) return void 0;
40
+ if (Date.now() > entry.expiresAt) {
41
+ this.cache.delete(key);
42
+ return void 0;
43
+ }
44
+ return entry.value;
45
+ }
46
+ has(key) {
47
+ return this.get(key) !== void 0;
48
+ }
49
+ };
50
+
51
+ // src/client.ts
52
+ var LOG_GROUP = `${FG_BLUE}[Localess:Client]${RESET}`;
53
+ function localessClient(options) {
54
+ if (options.debug) {
55
+ console.log(LOG_GROUP, "Client Options : ", options);
56
+ }
57
+ const fetchOptions = {
58
+ redirect: "follow",
59
+ headers: {
60
+ "Content-Type": "application/json",
61
+ "Accept": "application/json",
62
+ "X-Localess-Agent": "Localess-CLI-Client",
63
+ "X-Localess-Agent-Version": "0.9.0"
64
+ }
65
+ };
66
+ const cache = options.cacheTTL === false ? new NoCache() : new TTLCache(options.cacheTTL);
67
+ return {
68
+ async getSpace() {
69
+ if (options.debug) {
70
+ console.log(LOG_GROUP, "getSpace()");
71
+ }
72
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}?token=${options.token}`;
73
+ if (options.debug) {
74
+ console.log(LOG_GROUP, "getSpace fetch url : ", url);
75
+ }
76
+ if (cache.has(url)) {
77
+ if (options.debug) {
78
+ console.log(LOG_GROUP, "getSpace cache hit");
79
+ }
80
+ return cache.get(url);
81
+ }
82
+ try {
83
+ const response = await fetch(url, fetchOptions);
84
+ if (options.debug) {
85
+ console.log(LOG_GROUP, "getSpace status : ", response.status);
86
+ }
87
+ const data = await response.json();
88
+ cache.set(url, data);
89
+ return data;
90
+ } catch (error) {
91
+ console.error(LOG_GROUP, "getSpace error : ", error);
92
+ return {};
93
+ }
94
+ },
95
+ async getLinks(params) {
96
+ if (options.debug) {
97
+ console.log(LOG_GROUP, "getLinks() params : ", JSON.stringify(params));
98
+ }
99
+ let kind = "";
100
+ if (params?.kind) {
101
+ kind = `&kind=${params.kind}`;
102
+ }
103
+ let parentSlug = "";
104
+ if (params?.parentSlug) {
105
+ parentSlug = `&parentSlug=${params.parentSlug}`;
106
+ }
107
+ let excludeChildren = "";
108
+ if (params?.excludeChildren) {
109
+ excludeChildren = `&excludeChildren=${params.excludeChildren}`;
110
+ }
111
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/links?token=${options.token}${kind}${parentSlug}${excludeChildren}`;
112
+ if (options.debug) {
113
+ console.log(LOG_GROUP, "getLinks fetch url : ", url);
114
+ }
115
+ if (cache.has(url)) {
116
+ if (options.debug) {
117
+ console.log(LOG_GROUP, "getLinks cache hit");
118
+ }
119
+ return cache.get(url);
120
+ }
121
+ try {
122
+ const response = await fetch(url, fetchOptions);
123
+ if (options.debug) {
124
+ console.log(LOG_GROUP, "getLinks status : ", response.status);
125
+ }
126
+ const data = await response.json();
127
+ cache.set(url, data);
128
+ return data;
129
+ } catch (error) {
130
+ console.error(LOG_GROUP, "getLinks error : ", error);
131
+ return {};
132
+ }
133
+ },
134
+ async getContentBySlug(slug, params) {
135
+ if (options.debug) {
136
+ console.log(LOG_GROUP, "getContentBySlug() slug : ", slug);
137
+ console.log(LOG_GROUP, "getContentBySlug() params : ", JSON.stringify(params));
138
+ }
139
+ let version = "";
140
+ if (options?.version && options.version == "draft") {
141
+ version = `&version=${options.version}`;
142
+ }
143
+ if (params?.version && params.version == "draft") {
144
+ version = `&version=${params.version}`;
145
+ }
146
+ const locale = params?.locale ? `&locale=${params.locale}` : "";
147
+ const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
148
+ const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
149
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/slugs/${slug}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
150
+ if (options.debug) {
151
+ console.log(LOG_GROUP, "getContentBySlug fetch url : ", url);
152
+ }
153
+ if (cache.has(url)) {
154
+ if (options.debug) {
155
+ console.log(LOG_GROUP, "getContentBySlug cache hit");
156
+ }
157
+ return cache.get(url);
158
+ }
159
+ try {
160
+ const response = await fetch(url, fetchOptions);
161
+ if (options.debug) {
162
+ console.log(LOG_GROUP, "getContentBySlug status : ", response.status);
163
+ }
164
+ const data = await response.json();
165
+ cache.set(url, data);
166
+ return data;
167
+ } catch (error) {
168
+ console.error(LOG_GROUP, "getContentBySlug error : ", error);
169
+ return {};
170
+ }
171
+ },
172
+ async getContentById(id, params) {
173
+ if (options.debug) {
174
+ console.log(LOG_GROUP, "getContentById() id : ", id);
175
+ console.log(LOG_GROUP, "getContentById() params : ", JSON.stringify(params));
176
+ }
177
+ let version = "";
178
+ if (options?.version && options.version == "draft") {
179
+ version = `&version=${options.version}`;
180
+ }
181
+ if (params?.version && params.version == "draft") {
182
+ version = `&version=${params.version}`;
183
+ }
184
+ const locale = params?.locale ? `&locale=${params.locale}` : "";
185
+ const resolveReference = params?.resolveReference ? `&resolveReference=${params.resolveReference}` : "";
186
+ const resolveLink = params?.resolveLink ? `&resolveLink=${params.resolveLink}` : "";
187
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/contents/${id}?token=${options.token}${version}${locale}${resolveReference}${resolveLink}`;
188
+ if (options.debug) {
189
+ console.log(LOG_GROUP, "getContentById fetch url : ", url);
190
+ }
191
+ if (cache.has(url)) {
192
+ if (options.debug) {
193
+ console.log(LOG_GROUP, "getContentById cache hit");
194
+ }
195
+ return cache.get(url);
196
+ }
197
+ try {
198
+ const response = await fetch(url, fetchOptions);
199
+ if (options.debug) {
200
+ console.log(LOG_GROUP, "getContentById status : ", response.status);
201
+ }
202
+ const data = await response.json();
203
+ cache.set(url, data);
204
+ return data;
205
+ } catch (error) {
206
+ console.error(LOG_GROUP, "getContentById error : ", error);
207
+ return {};
208
+ }
209
+ },
210
+ async getTranslations(locale) {
211
+ if (options.debug) {
212
+ console.log(LOG_GROUP, "getTranslations() locale : ", locale);
213
+ }
214
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}?token=${options.token}`;
215
+ if (options.debug) {
216
+ console.log(LOG_GROUP, "getTranslations fetch url : ", url);
217
+ }
218
+ if (cache.has(url)) {
219
+ if (options.debug) {
220
+ console.log(LOG_GROUP, "getTranslations cache hit");
221
+ }
222
+ return cache.get(url);
223
+ }
224
+ try {
225
+ const response = await fetch(url, fetchOptions);
226
+ if (options.debug) {
227
+ console.log(LOG_GROUP, "getTranslations status : ", response.status);
228
+ }
229
+ const data = await response.json();
230
+ cache.set(url, data);
231
+ return data;
232
+ } catch (error) {
233
+ console.error(LOG_GROUP, "getTranslations error : ", error);
234
+ return {};
235
+ }
236
+ },
237
+ async updateTranslations(locale, type, values) {
238
+ if (options.debug) {
239
+ console.log(LOG_GROUP, "updateTranslations() locale : ", locale);
240
+ console.log(LOG_GROUP, "updateTranslations() type : ", type);
241
+ console.log(LOG_GROUP, "updateTranslations() values : ", JSON.stringify(values));
242
+ }
243
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/translations/${locale}`;
244
+ if (options.debug) {
245
+ console.log(LOG_GROUP, "updateTranslations fetch url : ", url);
246
+ }
247
+ const body = {
248
+ type,
249
+ values
250
+ };
251
+ try {
252
+ const response = await fetch(url, {
253
+ ...fetchOptions,
254
+ method: "POST",
255
+ headers: {
256
+ "X-API-KEY": options.token
257
+ },
258
+ body: JSON.stringify(body)
259
+ });
260
+ if (options.debug) {
261
+ console.log(LOG_GROUP, "updateTranslations status : ", response.status);
262
+ }
263
+ } catch (error) {
264
+ console.error(LOG_GROUP, "updateTranslations error : ", error);
265
+ }
266
+ },
267
+ async getOpenApi() {
268
+ if (options.debug) {
269
+ console.log(LOG_GROUP, "getOpenApi()");
270
+ }
271
+ let url = `${options.origin}/api/v1/spaces/${options.spaceId}/open-api?token=${options.token}`;
272
+ if (options.debug) {
273
+ console.log(LOG_GROUP, "getOpenApi fetch url : ", url);
274
+ }
275
+ if (cache.has(url)) {
276
+ if (options.debug) {
277
+ console.log(LOG_GROUP, "getTranslations cache hit");
278
+ }
279
+ return cache.get(url);
280
+ }
281
+ try {
282
+ const response = await fetch(url, fetchOptions);
283
+ if (options.debug) {
284
+ console.log(LOG_GROUP, "getOpenApi status : ", response.status);
285
+ }
286
+ const data = await response.json();
287
+ cache.set(url, data);
288
+ return data;
289
+ } catch (error) {
290
+ console.error(LOG_GROUP, "getOpenApi error : ", error);
291
+ return {};
292
+ }
293
+ },
294
+ syncScriptUrl() {
295
+ return `${options.origin}/scripts/sync-v1.js`;
296
+ },
297
+ assetLink(asset) {
298
+ if (typeof asset === "string") {
299
+ return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset}`;
300
+ } else {
301
+ return `${options.origin}/api/v1/spaces/${options.spaceId}/assets/${asset.uri}`;
302
+ }
303
+ }
304
+ };
305
+ }
306
+
307
+ // src/session.ts
308
+ import { access as access2, readFile } from "fs/promises";
309
+ import { join } from "path";
310
+ import * as process2 from "process";
311
+
312
+ // src/file.ts
313
+ import { access, constants, mkdir, writeFile } from "fs/promises";
314
+ import { parse } from "path";
315
+ var DEFAULT_CONFIG_DIR = ".localess";
316
+ async function writeToFile(filePath, data, option) {
317
+ const resolvedPath = parse(filePath).dir;
318
+ try {
319
+ await mkdir(resolvedPath, { recursive: true });
320
+ } catch (mkdirError) {
321
+ return;
322
+ }
323
+ try {
324
+ await writeFile(filePath, data, option);
325
+ } catch (writeError) {
326
+ }
327
+ }
328
+
329
+ // src/session.ts
330
+ var CREDENTIALS_PATH = join(process2.cwd(), DEFAULT_CONFIG_DIR, "credentials.json");
331
+ async function getSession() {
332
+ let session = {
333
+ isLoggedIn: false
334
+ };
335
+ const token = process2.env.LOCALESS_TOKEN;
336
+ const space = process2.env.LOCALESS_SPACE;
337
+ const origin = process2.env.LOCALESS_ORIGIN;
338
+ if (token && space && origin) {
339
+ console.debug(`Login in using environment variables.`);
340
+ return {
341
+ isLoggedIn: true,
342
+ space,
343
+ origin,
344
+ token,
345
+ method: "env"
346
+ };
347
+ }
348
+ try {
349
+ await access2(CREDENTIALS_PATH);
350
+ const content = await readFile(CREDENTIALS_PATH, "utf8");
351
+ const parsedContent = JSON.parse(content);
352
+ if (Object.keys(parsedContent).length === 0) {
353
+ return session;
354
+ }
355
+ if (parsedContent.origin && parsedContent.token && parsedContent.space) {
356
+ console.debug(`Login in using credentials file.`);
357
+ return {
358
+ isLoggedIn: true,
359
+ space: parsedContent.space,
360
+ origin: parsedContent.origin,
361
+ token: parsedContent.token,
362
+ method: "file"
363
+ };
364
+ }
365
+ } catch (e) {
366
+ console.error('No credentials found. Please log in using the "localess login" command.');
367
+ }
368
+ console.debug("Not logged in.");
369
+ return session;
370
+ }
371
+ async function persistSession(data) {
372
+ if (data.origin && data.token && data.space) {
373
+ await writeToFile(CREDENTIALS_PATH, JSON.stringify(data, null, 2), { mode: 384 });
374
+ console.log("Add session credentials to file system.");
375
+ console.log("Add .localess to .gitignore to avoid committing them to your repository.");
376
+ } else {
377
+ throw new Error("Cannot persist session: missing required fields.");
378
+ }
379
+ }
380
+ async function clearSession() {
381
+ try {
382
+ await access2(CREDENTIALS_PATH);
383
+ await writeToFile(CREDENTIALS_PATH, "{}", { mode: 384 });
384
+ } catch (error) {
385
+ throw new Error("Failed to clear session credentials.");
386
+ }
387
+ }
388
+
389
+ // src/commands/login/index.ts
390
+ var loginCommand = new Command("login").description("Login to Localess CLI").option("-t, --token <token>", "Token to login to Localess CLI").option("-s, --space <space>", "Space ID to login to").option("-o, --origin <origin>", "Origin of the Localess instance").action(async (options) => {
391
+ console.log("Logging in with options:", options);
392
+ const session = await getSession();
393
+ if (session.isLoggedIn && session.method === "file") {
394
+ console.log('Already logged in. If you want to log in with different credentials, please log out first using "localess logout" command.');
395
+ return;
396
+ }
397
+ if (options.origin && options.space && options.token) {
398
+ const client = localessClient({
399
+ origin: options.origin,
400
+ spaceId: options.space,
401
+ token: options.token
402
+ });
403
+ try {
404
+ const space = await client.getSpace();
405
+ console.log(`Successfully logged in to space: ${space.name} (${space.id})`);
406
+ await persistSession({
407
+ origin: options.origin,
408
+ space: options.space,
409
+ token: options.token
410
+ });
411
+ } catch (e) {
412
+ console.error("Login failed");
413
+ }
414
+ } else if (session.isLoggedIn && session.method === "env") {
415
+ console.log("Already logged in with environment variables.");
416
+ console.log("If you want to log in with different credentials, Please provide all required options: --origin, --space, and --token");
417
+ } else {
418
+ console.log("Please provide all required options: --origin, --space, and --token");
419
+ console.log("Or set the following environment variables: LOCALESS_ORIGIN, LOCALESS_SPACE, and LOCALESS_TOKEN");
420
+ }
421
+ });
422
+
423
+ // src/commands/logout/index.ts
424
+ import { Command as Command2 } from "commander";
425
+ var logoutCommand = new Command2("logout").description("Logout from Localess CLI").action(async () => {
426
+ console.log("Logging out...");
427
+ const session = await getSession();
428
+ if (!session.isLoggedIn) {
429
+ console.log("Not currently logged in.");
430
+ return;
431
+ }
432
+ if (session.method === "env") {
433
+ console.log("You are logged in using environment variables. To log out, unset LOCALESS_TOKEN, LOCALESS_SPACE, and LOCALESS_ORIGIN.");
434
+ return;
435
+ }
436
+ try {
437
+ await clearSession();
438
+ console.log("Successfully logged out.");
439
+ } catch (e) {
440
+ console.error("Failed to log out:", e);
441
+ }
442
+ });
443
+
444
+ // src/commands/types/index.ts
445
+ import { Command as Command3 } from "commander";
446
+ import openapiTS, { astToString } from "openapi-typescript";
447
+ import { join as join2 } from "path";
448
+ import process3 from "process";
449
+ var TYPES_PATH = join2(process3.cwd(), DEFAULT_CONFIG_DIR, "localess.d.ts");
450
+ var typesCommand = new Command3("types").description("Generate types for your schemas").action(async (options) => {
451
+ console.log("Types in with options:", options);
452
+ const session = await getSession();
453
+ if (!session.isLoggedIn) {
454
+ console.error("Not logged in");
455
+ console.error('Please log in first using "localess login" command');
456
+ return;
457
+ }
458
+ const client = localessClient({
459
+ origin: session.origin,
460
+ spaceId: session.space,
461
+ token: session.token
462
+ });
463
+ console.log("Fetching OpenAPI specification from Localess...");
464
+ const specification = await client.getOpenApi();
465
+ console.log("Generating types from OpenAPI specification...");
466
+ try {
467
+ const minimalSpec = {
468
+ openapi: "3.0.0",
469
+ info: { title: "Schemas Only", version: "1.0.0" },
470
+ components: { schemas: specification.components?.schemas || {} }
471
+ };
472
+ const ast = await openapiTS(minimalSpec, { exportType: true, rootTypes: true, rootTypesNoSchemaPrefix: true });
473
+ const contents = astToString(ast);
474
+ await writeToFile(TYPES_PATH, contents);
475
+ console.log(`Types generated successfully at ${TYPES_PATH}`);
476
+ } catch (e) {
477
+ console.error(e);
478
+ }
479
+ });
480
+
481
+ // src/program.ts
482
+ var program = new Command4();
483
+ program.name("Localess CLI").description("CLI tool for Localess platform management").version("0.0.1");
484
+ program.addCommand(loginCommand);
485
+ program.addCommand(logoutCommand);
486
+ program.addCommand(typesCommand);
487
+
488
+ // src/index.ts
489
+ try {
490
+ program.parse(process.argv);
491
+ } catch (e) {
492
+ console.error("Error executing command:", e instanceof Error ? e.message : e);
493
+ process.exit(1);
494
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@localess/cli",
3
+ "version": "0.0.1-dev.20260216094809",
4
+ "description": "Localess Command Line.",
5
+ "keywords": [
6
+ "localess",
7
+ "cli",
8
+ "node",
9
+ "javascript",
10
+ "typescript"
11
+ ],
12
+ "author": "Lessify",
13
+ "homepage": "https://github.com/Lessify/localess-js",
14
+ "sideEffects": false,
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.mjs"
22
+ }
23
+ },
24
+ "main": "./dist/index.mjs",
25
+ "types": "./dist/index.d.ts",
26
+ "bin": {
27
+ "localess": "./dist/index.mjs"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/Lessify/localess-js.git",
32
+ "directory": "packages/cli"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/Lessify/localess-js/issues"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format cjs,esm --dts --shims",
39
+ "test": "vitest"
40
+ },
41
+ "license": "MIT",
42
+ "dependencies": {
43
+ "commander": "^14.0.3",
44
+ "chalk": "^5.6.2",
45
+ "openapi-typescript": "^7.12.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "^5.0.0"
51
+ },
52
+ "engines": {
53
+ "node": ">= 20.0.0"
54
+ }
55
+ }