@contentstorage/core 3.1.0 → 3.2.1

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 CHANGED
@@ -39,13 +39,21 @@ Create `contentstorage.config.js` in your project root:
39
39
 
40
40
  ```javascript
41
41
  export default {
42
- contentKey: 'your-content-key',
42
+ // Option A: Content Key (read-only access)
43
+ contentKey: 'your-team-id/your-project-id',
44
+
45
+ // Option B: API Key (read + write access) - recommended for CI/CD
46
+ // apiKey: 'csk_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx',
47
+ // projectId: 'your-project-uuid',
48
+
43
49
  languageCodes: ['EN', 'FR', 'DE'],
44
50
  contentDir: 'src/content/json',
45
51
  typesOutputFile: 'src/content/content-types.d.ts'
46
52
  };
47
53
  ```
48
54
 
55
+ > **Note:** Use `contentKey` for read-only access (pull, stats). Use `apiKey` + `projectId` for full access including push operations. API keys can be created in Project Settings → API Keys.
56
+
49
57
  ### 2. Pull Content & Generate Types
50
58
 
51
59
  ```bash
@@ -86,18 +94,59 @@ npx contentstorage pull [options]
86
94
  ```
87
95
 
88
96
  **Options:**
89
- - `--content-key <key>` - Content key for your project
97
+ - `--content-key <key>` - Content key (read-only access)
98
+ - `--api-key <key>` - API key (read + write access)
99
+ - `--project-id <id>` - Project ID (required with --api-key)
90
100
  - `--content-dir <dir>` - Directory to save content files
91
101
  - `--lang <code>` - Language code (e.g., EN, FR)
92
102
  - `--pending-changes` - Fetch pending/draft content
103
+ - `--flatten` - Output flattened key-value pairs
93
104
 
94
105
  **Examples:**
95
106
  ```bash
96
- npx contentstorage pull --content-key abc123
107
+ # Using content key (read-only)
108
+ npx contentstorage pull --content-key teamId/projectId
109
+
110
+ # Using API key (recommended for CI/CD)
111
+ npx contentstorage pull --api-key csk_xxx --project-id uuid
112
+
113
+ # Pull specific language with draft content
97
114
  npx contentstorage pull --lang EN --pending-changes
98
- npx contentstorage pull --content-dir src/content
99
115
  ```
100
116
 
117
+ ### `contentstorage push`
118
+
119
+ Push local content changes to Contentstorage (requires API key)
120
+
121
+ ```bash
122
+ npx contentstorage push [options]
123
+ ```
124
+
125
+ **Options:**
126
+ - `--api-key <key>` - API key (required)
127
+ - `--project-id <id>` - Project ID (required)
128
+ - `--content-dir <dir>` - Directory with content files
129
+ - `--lang <code>` - Language code to push (e.g., EN)
130
+ - `--dry-run` - Preview changes without applying
131
+
132
+ **Examples:**
133
+ ```bash
134
+ # Push all configured languages
135
+ npx contentstorage push --api-key csk_xxx --project-id uuid
136
+
137
+ # Push specific language
138
+ npx contentstorage push --api-key csk_xxx --project-id uuid --lang EN
139
+
140
+ # Preview changes without applying
141
+ npx contentstorage push --api-key csk_xxx --project-id uuid --dry-run
142
+ ```
143
+
144
+ **Behavior:**
145
+ - Adds new keys that don't exist in Contentstorage
146
+ - Updates existing keys with different values
147
+ - Never removes keys (safe by design)
148
+ - If change tracking is enabled, creates pending changes for review
149
+
101
150
  ### `contentstorage generate-types`
102
151
 
103
152
  Generate TypeScript type definitions from content
@@ -127,15 +176,20 @@ npx contentstorage stats [options]
127
176
  ```
128
177
 
129
178
  **Options:**
130
- - `--content-key <key>` - Content key for your project
179
+ - `--content-key <key>` - Content key (read-only access)
180
+ - `--api-key <key>` - API key (fetches stats directly from API)
181
+ - `--project-id <id>` - Project ID (required with --api-key)
131
182
  - `--content-dir <dir>` - Directory with content files
132
183
  - `--pending-changes` - Analyze pending/draft content
133
184
 
134
185
  **Examples:**
135
186
  ```bash
187
+ # Using local files + content key
136
188
  npx contentstorage stats
137
- npx contentstorage stats --content-key abc123
138
- npx contentstorage stats --pending-changes
189
+ npx contentstorage stats --content-key teamId/projectId
190
+
191
+ # Using API key (fetches directly from server)
192
+ npx contentstorage stats --api-key csk_xxx --project-id uuid
139
193
  ```
140
194
 
141
195
  **What it shows:**
@@ -168,8 +222,14 @@ npx contentstorage stats --help
168
222
 
169
223
  ```javascript
170
224
  export default {
171
- // Required: Unique content identifier
172
- contentKey: 'your-content-key',
225
+ // Authentication Option A: Content Key (read-only)
226
+ // Format: teamId/projectId
227
+ contentKey: 'your-team-id/your-project-id',
228
+
229
+ // Authentication Option B: API Key (read + write)
230
+ // Create in Project Settings → API Keys
231
+ // apiKey: 'csk_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx',
232
+ // projectId: 'your-project-uuid',
173
233
 
174
234
  // Required: Array of language codes
175
235
  languageCodes: ['EN', 'FR', 'DE', 'ES'],
@@ -185,6 +245,17 @@ export default {
185
245
  };
186
246
  ```
187
247
 
248
+ ### Authentication Methods
249
+
250
+ | Method | Access Level | Use Case |
251
+ |--------|-------------|----------|
252
+ | `contentKey` | Read-only | Pull content, view stats |
253
+ | `apiKey` + `projectId` | Read + Write | Pull, push, CI/CD pipelines |
254
+
255
+ **Getting your credentials:**
256
+ - **Content Key**: Found in Project Settings → General (format: `teamId/projectId`)
257
+ - **API Key**: Create in Project Settings → API Keys
258
+
188
259
  ### Supported Languages
189
260
 
190
261
  The CLI supports 40+ languages including:
@@ -5,6 +5,7 @@ import { pushContent } from './push.js';
5
5
  import { generateTypes } from './generate-types.js';
6
6
  import { showStats } from './stats.js';
7
7
  import { captureScreenshot } from './screenshot.js';
8
+ import { translateContent } from './translate.js';
8
9
  const COMMANDS = {
9
10
  pull: {
10
11
  name: 'pull',
@@ -14,6 +15,7 @@ const COMMANDS = {
14
15
  ' --content-key <key> Content key (read-only access)',
15
16
  ' --api-key <key> API key (read + write access)',
16
17
  ' --project-id <id> Project ID (required with --api-key)',
18
+ ' --api-url <url> API base URL (default: production)',
17
19
  ' --content-dir <dir> Directory to save content files',
18
20
  ' --lang <code> Language code (e.g., EN, FR)',
19
21
  ' --pending-changes Fetch pending/draft content',
@@ -27,6 +29,7 @@ const COMMANDS = {
27
29
  options: [
28
30
  ' --api-key <key> API key for authentication (required)',
29
31
  ' --project-id <id> Project ID (required)',
32
+ ' --api-url <url> API base URL (default: production)',
30
33
  ' --content-dir <dir> Directory with content files',
31
34
  ' --lang <code> Language code to push (e.g., EN)',
32
35
  ' --dry-run Preview changes without applying',
@@ -51,6 +54,7 @@ const COMMANDS = {
51
54
  ' --content-key <key> Content key (read-only access)',
52
55
  ' --api-key <key> API key (read + write access)',
53
56
  ' --project-id <id> Project ID (required with --api-key)',
57
+ ' --api-url <url> API base URL (default: production)',
54
58
  ' --content-dir <dir> Directory with content files',
55
59
  ' --pending-changes Analyze pending/draft content',
56
60
  ],
@@ -64,6 +68,20 @@ const COMMANDS = {
64
68
  ' --content-key <key> Content key for your project',
65
69
  ],
66
70
  },
71
+ translate: {
72
+ name: 'translate',
73
+ description: 'Push new/changed keys and create a translate session',
74
+ usage: 'contentstorage translate [options]',
75
+ options: [
76
+ ' --api-key <key> API key for authentication',
77
+ ' --project-id <id> Project ID',
78
+ ' --api-url <url> API base URL (default: production)',
79
+ ' --content-dir <dir> Directory with content files',
80
+ ' --lang <code> Source language code (e.g., EN)',
81
+ ' --dry-run Preview changes without pushing',
82
+ ' -y Skip confirmation prompt',
83
+ ],
84
+ },
67
85
  };
68
86
  function showHelp() {
69
87
  console.log(chalk.bold('\nContentstorage CLI'));
@@ -132,6 +150,9 @@ async function main() {
132
150
  case 'screenshot':
133
151
  await captureScreenshot();
134
152
  break;
153
+ case 'translate':
154
+ await translateContent();
155
+ break;
135
156
  default:
136
157
  console.error(chalk.red(`Unknown command: ${command}\n`));
137
158
  console.log(chalk.dim('Run "contentstorage --help" for usage'));
@@ -38,6 +38,9 @@ export async function pullContent() {
38
38
  else if (key === 'project-id') {
39
39
  cliConfig.projectId = value;
40
40
  }
41
+ else if (key === 'api-url') {
42
+ cliConfig.apiUrl = value;
43
+ }
41
44
  // Skip the value in the next iteration
42
45
  i++;
43
46
  }
@@ -78,6 +81,7 @@ export async function pullContent() {
78
81
  ? createApiClient({
79
82
  apiKey: config.apiKey,
80
83
  projectId: config.projectId,
84
+ apiUrl: config.apiUrl,
81
85
  })
82
86
  : null;
83
87
  // If using API client and no languages specified, fetch from project info
@@ -30,6 +30,9 @@ export async function pushContent() {
30
30
  else if (key === 'content-dir') {
31
31
  cliConfig.contentDir = value;
32
32
  }
33
+ else if (key === 'api-url') {
34
+ cliConfig.apiUrl = value;
35
+ }
33
36
  i++; // Skip the value in next iteration
34
37
  }
35
38
  }
@@ -69,6 +72,7 @@ export async function pushContent() {
69
72
  const apiClient = createApiClient({
70
73
  apiKey: config.apiKey,
71
74
  projectId: config.projectId,
75
+ apiUrl: config.apiUrl,
72
76
  });
73
77
  if (!apiClient) {
74
78
  console.error(chalk.red('Failed to create API client'));
@@ -267,6 +267,9 @@ export async function showStats() {
267
267
  else if (key === 'project-id') {
268
268
  cliConfig.projectId = value;
269
269
  }
270
+ else if (key === 'api-url') {
271
+ cliConfig.apiUrl = value;
272
+ }
270
273
  i++; // Skip the value in next iteration
271
274
  }
272
275
  }
@@ -296,6 +299,7 @@ export async function showStats() {
296
299
  const apiClient = createApiClient({
297
300
  apiKey: config.apiKey,
298
301
  projectId: config.projectId,
302
+ apiUrl: config.apiUrl,
299
303
  });
300
304
  if (!apiClient) {
301
305
  console.error(chalk.red('Failed to create API client'));
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function translateContent(): Promise<void>;
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import readline from 'readline';
5
+ import { exec, execSync } from 'child_process';
6
+ import chalk from 'chalk';
7
+ import { loadConfig } from '../core/config-loader.js';
8
+ import { createApiClient } from '../core/api-client.js';
9
+ import { flattenJson } from '../utils/flatten-json.js';
10
+ function prompt(question) {
11
+ const rl = readline.createInterface({
12
+ input: process.stdin,
13
+ output: process.stdout,
14
+ });
15
+ return new Promise((resolve) => {
16
+ rl.question(question, (answer) => {
17
+ rl.close();
18
+ resolve(answer.trim());
19
+ });
20
+ });
21
+ }
22
+ function getGitBranch() {
23
+ try {
24
+ return execSync('git rev-parse --abbrev-ref HEAD', {
25
+ encoding: 'utf-8',
26
+ stdio: ['pipe', 'pipe', 'pipe'],
27
+ }).trim();
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ function openInBrowser(url) {
34
+ const platform = process.platform;
35
+ let command;
36
+ if (platform === 'darwin') {
37
+ command = `open "${url}"`;
38
+ }
39
+ else if (platform === 'win32') {
40
+ command = `start "" "${url}"`;
41
+ }
42
+ else {
43
+ command = `xdg-open "${url}"`;
44
+ }
45
+ exec(command, (error) => {
46
+ if (error) {
47
+ console.error(chalk.red(`Failed to open browser: ${error.message}`));
48
+ console.log(chalk.yellow(`Please open this URL manually:\n ${url}`));
49
+ }
50
+ });
51
+ }
52
+ export async function translateContent() {
53
+ // Parse CLI arguments
54
+ const args = process.argv.slice(2);
55
+ const cliConfig = {};
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+ if (arg === '-y') {
59
+ cliConfig.skipConfirm = true;
60
+ }
61
+ else if (arg.startsWith('--')) {
62
+ const key = arg.substring(2);
63
+ const value = args[i + 1];
64
+ if (key === 'dry-run') {
65
+ cliConfig.dryRun = true;
66
+ }
67
+ else if (value && !value.startsWith('--') && value !== '-y') {
68
+ if (key === 'lang') {
69
+ cliConfig.languageCodes = [value.toUpperCase()];
70
+ }
71
+ else if (key === 'api-key') {
72
+ cliConfig.apiKey = value;
73
+ }
74
+ else if (key === 'project-id') {
75
+ cliConfig.projectId = value;
76
+ }
77
+ else if (key === 'content-dir') {
78
+ cliConfig.contentDir = value;
79
+ }
80
+ else if (key === 'api-url') {
81
+ cliConfig.apiUrl = value;
82
+ }
83
+ i++;
84
+ }
85
+ }
86
+ }
87
+ // Load config
88
+ let fileConfig = {};
89
+ try {
90
+ fileConfig = await loadConfig();
91
+ }
92
+ catch {
93
+ console.log(chalk.yellow('Could not load configuration file. Using CLI arguments only.'));
94
+ }
95
+ const config = { ...fileConfig, ...cliConfig };
96
+ // Validate required fields
97
+ if (!config.apiKey) {
98
+ console.error(chalk.red('\n❌ Error: API key is required.\n' +
99
+ ' Provide it via config file (apiKey) or --api-key argument.\n' +
100
+ ' You can create an API key in Project Settings → API Keys.'));
101
+ process.exit(1);
102
+ }
103
+ if (!config.projectId) {
104
+ console.error(chalk.red('\n❌ Error: Project ID is required.\n' +
105
+ ' Provide it via config file (projectId) or --project-id argument.'));
106
+ process.exit(1);
107
+ }
108
+ if (!config.contentDir) {
109
+ console.error(chalk.red('\n❌ Error: Content directory is required.\n' +
110
+ ' Provide it via config file (contentDir) or --content-dir argument.'));
111
+ process.exit(1);
112
+ }
113
+ if (!config.languageCodes || config.languageCodes.length === 0) {
114
+ console.error(chalk.red('\n❌ Error: At least one language code is required.\n' +
115
+ ' Provide it via config file (languageCodes) or --lang argument.'));
116
+ process.exit(1);
117
+ }
118
+ const sourceLanguage = config.languageCodes[0];
119
+ // Create API client
120
+ const apiClient = createApiClient({
121
+ apiKey: config.apiKey,
122
+ projectId: config.projectId,
123
+ apiUrl: config.apiUrl,
124
+ });
125
+ if (!apiClient) {
126
+ console.error(chalk.red('Failed to create API client'));
127
+ process.exit(1);
128
+ }
129
+ // Step 1: Read local source file
130
+ const filePath = path.join(config.contentDir, `${sourceLanguage}.json`);
131
+ let localContent;
132
+ try {
133
+ const fileContent = await fs.readFile(filePath, 'utf-8');
134
+ localContent = JSON.parse(fileContent);
135
+ }
136
+ catch (error) {
137
+ if (error.code === 'ENOENT') {
138
+ console.error(chalk.red(`\n❌ Source file not found: ${filePath}\n` +
139
+ ` Check your contentDir setting and ensure the file exists.`));
140
+ }
141
+ else {
142
+ console.error(chalk.red(`\n❌ Error reading source file: ${error.message}`));
143
+ }
144
+ process.exit(1);
145
+ }
146
+ const localFlat = flattenJson(localContent);
147
+ // Step 2: Fetch server content
148
+ console.log(chalk.blue('Comparing with server...'));
149
+ let serverFlat;
150
+ try {
151
+ const serverResponse = await apiClient.getContent({
152
+ languageCode: sourceLanguage,
153
+ format: 'flat',
154
+ draft: true,
155
+ });
156
+ serverFlat = (serverResponse.data[sourceLanguage] || {});
157
+ }
158
+ catch (error) {
159
+ console.error(chalk.red(`\n❌ Could not fetch server content: ${error.message}`));
160
+ process.exit(1);
161
+ }
162
+ // Step 3: Compute diff
163
+ const diff = { newKeys: [], modifiedKeys: [] };
164
+ for (const [key, value] of Object.entries(localFlat)) {
165
+ const serverValue = serverFlat[key];
166
+ if (serverValue === undefined) {
167
+ diff.newKeys.push({ key, value });
168
+ }
169
+ else if (serverValue !== value) {
170
+ diff.modifiedKeys.push({ key, value, oldValue: serverValue });
171
+ }
172
+ }
173
+ const totalChanges = diff.newKeys.length + diff.modifiedKeys.length;
174
+ if (totalChanges === 0) {
175
+ console.log(chalk.green('\n✓ All keys are up to date. Nothing to translate.'));
176
+ process.exit(0);
177
+ }
178
+ // Step 4: Display diff
179
+ const parts = [];
180
+ if (diff.newKeys.length > 0)
181
+ parts.push(`${diff.newKeys.length} new`);
182
+ if (diff.modifiedKeys.length > 0)
183
+ parts.push(`${diff.modifiedKeys.length} modified`);
184
+ console.log(chalk.bold(`\nFound ${parts.join(', ')}:\n`));
185
+ for (const { key, value } of diff.newKeys) {
186
+ const displayValue = value.length > 50 ? value.substring(0, 47) + '...' : value;
187
+ console.log(chalk.green(` + ${key.padEnd(35)} ${chalk.dim(`"${displayValue}"`)}`));
188
+ }
189
+ for (const { key, value, oldValue } of diff.modifiedKeys) {
190
+ const displayOld = oldValue.length > 25 ? oldValue.substring(0, 22) + '...' : oldValue;
191
+ const displayNew = value.length > 25 ? value.substring(0, 22) + '...' : value;
192
+ console.log(chalk.yellow(` ~ ${key.padEnd(35)} ${chalk.dim(`"${displayOld}" → "${displayNew}"`)}`));
193
+ }
194
+ // Dry run stops here
195
+ if (config.dryRun) {
196
+ console.log(chalk.dim('\n(dry run — nothing was pushed)'));
197
+ process.exit(0);
198
+ }
199
+ // Step 5: Confirm
200
+ if (!config.skipConfirm) {
201
+ // Get project name for display
202
+ let projectName = config.projectId;
203
+ try {
204
+ const projectInfo = await apiClient.getProject();
205
+ projectName = projectInfo.project.name;
206
+ }
207
+ catch {
208
+ // Use projectId as fallback
209
+ }
210
+ console.log(chalk.dim(`\nThese will be pushed to Contentstorage (project: ${projectName}).`));
211
+ const answer = await prompt('Proceed? (Y/n) ');
212
+ if (answer.toLowerCase() === 'n') {
213
+ console.log(chalk.dim('Cancelled.'));
214
+ process.exit(0);
215
+ }
216
+ }
217
+ // Step 6: Push with session
218
+ const sessionKeys = [
219
+ ...diff.newKeys.map(({ key, value }) => ({ key, value, status: 'new' })),
220
+ ...diff.modifiedKeys.map(({ key, value }) => ({ key, value, status: 'modified' })),
221
+ ];
222
+ const gitBranch = getGitBranch();
223
+ try {
224
+ const response = await apiClient.pushContent(sourceLanguage, localContent, {
225
+ session: {
226
+ keys: sessionKeys,
227
+ metadata: {
228
+ ...(gitBranch ? { gitBranch } : {}),
229
+ pushedAt: new Date().toISOString(),
230
+ },
231
+ },
232
+ });
233
+ const { result } = response;
234
+ const pushed = result.keysAdded + result.keysUpdated;
235
+ console.log(chalk.green(`\n✓ ${pushed} keys pushed (${result.keysAdded} new, ${result.keysUpdated} modified)`) +
236
+ (response.session ? chalk.dim(` [session: ${response.session.id}]`) : ''));
237
+ // Step 7: Show session URL and ask before opening browser
238
+ if (response.session) {
239
+ console.log(chalk.bold('\n→ Translate session created:'));
240
+ console.log(chalk.cyan(` ${response.session.url}`));
241
+ const openAnswer = await prompt('Open in browser? (Y/n) ');
242
+ if (openAnswer.toLowerCase() !== 'n') {
243
+ openInBrowser(response.session.url);
244
+ }
245
+ }
246
+ }
247
+ catch (error) {
248
+ console.error(chalk.red(`\n❌ Failed to push keys: ${error.message}`));
249
+ process.exit(1);
250
+ }
251
+ }
@@ -20,10 +20,26 @@ export interface PushResult {
20
20
  pendingChangesCreated: boolean;
21
21
  uploadId?: string;
22
22
  }
23
+ export interface TranslateSessionData {
24
+ keys: Array<{
25
+ key: string;
26
+ value: string;
27
+ status: 'new' | 'modified';
28
+ }>;
29
+ metadata?: {
30
+ gitBranch?: string;
31
+ pushedAt?: string;
32
+ };
33
+ }
34
+ export interface TranslateSessionResult {
35
+ id: string;
36
+ url: string;
37
+ }
23
38
  export interface PushResponse {
24
39
  success: boolean;
25
40
  result: PushResult;
26
41
  dryRun?: boolean;
42
+ session?: TranslateSessionResult;
27
43
  }
28
44
  export interface ProjectResponse {
29
45
  success: boolean;
@@ -61,7 +77,7 @@ export interface ApiError {
61
77
  export declare class ApiClient {
62
78
  private client;
63
79
  private projectId;
64
- constructor(apiKey: string, projectId: string);
80
+ constructor(apiKey: string, projectId: string, apiUrl?: string);
65
81
  /**
66
82
  * Handle API errors consistently
67
83
  */
@@ -85,6 +101,7 @@ export declare class ApiClient {
85
101
  */
86
102
  pushContent(languageCode: string, content: Record<string, any>, options?: {
87
103
  dryRun?: boolean;
104
+ session?: TranslateSessionData;
88
105
  }): Promise<PushResponse>;
89
106
  /**
90
107
  * Get project information
@@ -102,4 +119,5 @@ export declare class ApiClient {
102
119
  export declare function createApiClient(config: {
103
120
  apiKey?: string;
104
121
  projectId?: string;
122
+ apiUrl?: string;
105
123
  }): ApiClient | null;
@@ -5,10 +5,11 @@
5
5
  import axios from 'axios';
6
6
  import { CONTENTSTORAGE_CONFIG } from '../utils/constants.js';
7
7
  export class ApiClient {
8
- constructor(apiKey, projectId) {
8
+ constructor(apiKey, projectId, apiUrl) {
9
9
  this.projectId = projectId;
10
+ const baseApiUrl = apiUrl || CONTENTSTORAGE_CONFIG.API_URL;
10
11
  this.client = axios.create({
11
- baseURL: `${CONTENTSTORAGE_CONFIG.API_URL}/api/v1/cli`,
12
+ baseURL: `${baseApiUrl}/api/v1/cli`,
12
13
  headers: {
13
14
  Authorization: `Bearer ${apiKey}`,
14
15
  'Content-Type': 'application/json',
@@ -63,14 +64,18 @@ export class ApiClient {
63
64
  */
64
65
  async pushContent(languageCode, content, options = {}) {
65
66
  try {
66
- const response = await this.client.post('/content/push', {
67
+ const body = {
67
68
  projectId: this.projectId,
68
69
  languageCode,
69
70
  content,
70
71
  options: {
71
72
  dryRun: options.dryRun || false,
72
73
  },
73
- });
74
+ };
75
+ if (options.session) {
76
+ body.session = options.session;
77
+ }
78
+ const response = await this.client.post('/content/push', body);
74
79
  return response.data;
75
80
  }
76
81
  catch (error) {
@@ -110,5 +115,5 @@ export function createApiClient(config) {
110
115
  if (!config.apiKey || !config.projectId) {
111
116
  return null;
112
117
  }
113
- return new ApiClient(config.apiKey, config.projectId);
118
+ return new ApiClient(config.apiKey, config.projectId, config.apiUrl);
114
119
  }
@@ -40,6 +40,7 @@ export async function loadConfig() {
40
40
  // New API key authentication fields
41
41
  apiKey: mergedConfig.apiKey,
42
42
  projectId: mergedConfig.projectId,
43
+ apiUrl: process.env.CONTENTSTORAGE_API_URL || mergedConfig.apiUrl,
43
44
  };
44
45
  return finalConfig;
45
46
  }
package/dist/types.d.ts CHANGED
@@ -7,6 +7,7 @@ export type AppConfig = {
7
7
  flatten?: boolean;
8
8
  apiKey?: string;
9
9
  projectId?: string;
10
+ apiUrl?: string;
10
11
  };
11
12
  export type LanguageCode = 'SQ' | 'BE' | 'BS' | 'BG' | 'HR' | 'CS' | 'DA' | 'NL' | 'EN' | 'ET' | 'FI' | 'FR' | 'DE' | 'EL' | 'HU' | 'GA' | 'IT' | 'LV' | 'LT' | 'MK' | 'NO' | 'PL' | 'PT' | 'RO' | 'RU' | 'SR' | 'SK' | 'SL' | 'ES' | 'SV' | 'TR' | 'UK';
12
13
  /**
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@contentstorage/core",
3
3
  "author": "Kaido Hussar <kaido@contentstorage.app>",
4
4
  "homepage": "https://contentstorage.app",
5
- "version": "3.1.0",
5
+ "version": "3.2.1",
6
6
  "type": "module",
7
7
  "description": "Contentstorage CLI for managing translations and generating TypeScript types",
8
8
  "license": "MIT",