@strata-ds/core 2.2.1 → 2.2.2

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
@@ -348,16 +348,27 @@ npx strata init
348
348
 
349
349
  This generates a `strata.json` configuration file:
350
350
 
351
+ During initialization, the CLI asks whether the project is public or private. Public projects can update their website URL without a token; for a private project, the CLI requests your sync token (with hidden input) when you provide a website URL. The URL is then sent to the backend:
352
+
353
+ ```bash
354
+ npx strata set url
355
+ # or provide the address directly
356
+ npx strata set url example.com
357
+ ```
358
+
359
+ If the address has no scheme, Strata adds `https://`. `npx strata set url` asks whether the project is public or private every time; the value saved in `strata.json` is offered as the default, which you can accept or change. Public projects send no sync token; private projects use `STRATA_SYNC_TOKEN` or `syncToken` from `strata.json` when set, otherwise prompting for the token without displaying it. Older configuration files without `projectType` prompt for a choice without a default. The token is not written to `strata.json` by `init`.
360
+
351
361
  ```json
352
362
  {
353
363
  "projectId": "YOUR_PROJECT_ID",
364
+ "projectType": "public",
354
365
  "snapshotCdnBase": "https://cdn.strata.charisol.io",
355
366
  "appName": "Storefront Web",
356
367
  "reportUsage": true
357
368
  }
358
369
  ```
359
370
 
360
- When `strata.json` is present, the `@strata-ds/core` `postinstall` script automatically registers build-time presence for your application.
371
+ When `strata.json` is present, the `@strata-ds/core` `postinstall` script automatically registers build-time presence for your application. The website URL can also be updated from the project dashboard's Settings.
361
372
 
362
373
  ## Using Design System Components
363
374
 
@@ -136,6 +136,7 @@ Usage:
136
136
 
137
137
  Commands:
138
138
  init Interactively initialize strata.json configuration file
139
+ set url [website] Update the project website URL
139
140
  run Fetch snapshot and generate fallbacks using strata.json
140
141
  snapshot Legacy command to fetch snapshot using options
141
142
 
@@ -159,6 +160,117 @@ function askQuestion(query) {
159
160
  resolve(ans.trim());
160
161
  }));
161
162
  }
163
+ async function askProjectPrivacy(defaultValue) {
164
+ console.log('Project visibility determines whether a sync token is needed:\n' +
165
+ ' Public — readable without a sync token\n' +
166
+ ' Private — protected; a sync token is required to update its website URL');
167
+ while (true) {
168
+ const defaultHint = defaultValue ? ` [${defaultValue}]` : '';
169
+ const answer = (await askQuestion(`Is your Strata project public or private? (public/private)${defaultHint}: `)).toLowerCase() || defaultValue;
170
+ if (answer === 'public' || answer === 'private')
171
+ return answer;
172
+ console.log('Please enter "public" or "private".');
173
+ }
174
+ }
175
+ function askSecret(query) {
176
+ const input = process.stdin;
177
+ if (!input.isTTY || typeof input.setRawMode !== 'function') {
178
+ throw new Error('No interactive terminal. Set STRATA_SYNC_TOKEN or add syncToken to strata.json.');
179
+ }
180
+ return new Promise((resolve, reject) => {
181
+ let value = '';
182
+ const cleanup = () => {
183
+ input.removeListener('data', onData);
184
+ input.setRawMode(false);
185
+ input.pause();
186
+ };
187
+ const onData = (chunk) => {
188
+ const key = chunk.toString();
189
+ if (key === '\u0003') {
190
+ cleanup();
191
+ process.stdout.write('\n');
192
+ reject(new Error('Cancelled.'));
193
+ }
194
+ else if (key === '\r' || key === '\n') {
195
+ cleanup();
196
+ process.stdout.write('\n');
197
+ resolve(value.trim());
198
+ }
199
+ else if (key === '\u007f' || key === '\b') {
200
+ value = value.slice(0, -1);
201
+ process.stdout.write('\b \b');
202
+ }
203
+ else {
204
+ value += key;
205
+ process.stdout.write('*');
206
+ }
207
+ };
208
+ process.stdout.write(query);
209
+ input.setRawMode(true);
210
+ input.resume();
211
+ input.on('data', onData);
212
+ });
213
+ }
214
+ function normalizeWebsiteUrl(value) {
215
+ const trimmed = value.trim();
216
+ if (!trimmed)
217
+ return null;
218
+ const candidate = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
219
+ try {
220
+ const url = new URL(candidate);
221
+ const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
222
+ if (!['http:', 'https:'].includes(url.protocol) ||
223
+ !url.hostname ||
224
+ url.username ||
225
+ url.password ||
226
+ !hostname.includes('.') ||
227
+ hostname.includes(':') ||
228
+ /^(?:\d{1,3}\.){3}\d{1,3}$/.test(hostname) ||
229
+ /(?:^|\.)localhost$|\.local$|\.internal$/.test(hostname)) {
230
+ return null;
231
+ }
232
+ return candidate;
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ }
238
+ async function getSyncToken(config) {
239
+ const configuredToken = process.env.STRATA_SYNC_TOKEN || config.syncToken;
240
+ const token = configuredToken || await askSecret('Enter your Strata Sync Token (input hidden): ');
241
+ if (!token.trim())
242
+ throw new Error('A Strata Sync Token is required to update the website URL.');
243
+ return token.trim();
244
+ }
245
+ async function saveWebsiteUrl(projectId, websiteUrl, syncToken, apiBase) {
246
+ const base = (apiBase || process.env.STRATA_API_BASE || 'https://api-production-charisol-design-system-be.aws.charisol.io')
247
+ .replace(/\/+$/, '')
248
+ .replace(/\/v1$/i, '');
249
+ const headers = {
250
+ 'Content-Type': 'application/json',
251
+ };
252
+ if (syncToken) {
253
+ headers.Authorization = `Bearer ${syncToken}`;
254
+ }
255
+ const response = await fetch(`${base}/v1/connect/${encodeURIComponent(projectId)}/website-url`, {
256
+ method: 'PATCH',
257
+ headers,
258
+ body: JSON.stringify({ websiteUrl }),
259
+ });
260
+ const contentType = response.headers.get('content-type') || '';
261
+ if (!contentType.includes('application/json')) {
262
+ throw new Error(`The API returned ${response.status} ${response.statusText} instead of JSON. ` +
263
+ `Check STRATA_API_BASE or apiBase in strata.json; it should point to the backend API host, optionally with a /v1 suffix.`);
264
+ }
265
+ const result = await response.json();
266
+ if (!response.ok || !result.status) {
267
+ throw new Error(result.message || `The API returned ${response.status} ${response.statusText}.`);
268
+ }
269
+ if (typeof result.data?.websiteUrl !== 'string') {
270
+ throw new Error('The API response did not contain the saved website URL.');
271
+ }
272
+ return result.data.websiteUrl;
273
+ }
162
274
  async function runInit() {
163
275
  console.log('Strata Design System Initialization\n');
164
276
  let projectId = '';
@@ -168,6 +280,18 @@ async function runInit() {
168
280
  console.log('Error: Project ID is required.');
169
281
  }
170
282
  }
283
+ const projectType = await askProjectPrivacy();
284
+ let websiteUrl = null;
285
+ while (true) {
286
+ const input = await askQuestion('Enter your website URL (optional, e.g. example.com): ');
287
+ if (!input)
288
+ break;
289
+ websiteUrl = normalizeWebsiteUrl(input);
290
+ if (websiteUrl)
291
+ break;
292
+ console.log('Error: Enter a public website hostname or HTTP(S) URL.');
293
+ }
294
+ const syncToken = websiteUrl && projectType === 'private' ? await getSyncToken({}) : undefined;
171
295
  const cdnBase = (await askQuestion('Enter CDN Base URL (default: https://dx2nhw7wqn4nn.cloudfront.net): ')) || 'https://dx2nhw7wqn4nn.cloudfront.net';
172
296
  const output = (await askQuestion('Enter path to write snapshot JSON file (default: ./src/data/strata-snapshot.json): ')) || './src/data/strata-snapshot.json';
173
297
  const outputCss = (await askQuestion('Enter path to write CSS fallback file (default: ./src/styles/strata-fallbacks.css): ')) || './src/styles/strata-fallbacks.css';
@@ -183,15 +307,57 @@ async function runInit() {
183
307
  outputCss,
184
308
  variablesOnly,
185
309
  reportUsage,
310
+ projectType,
186
311
  };
312
+ if (websiteUrl) {
313
+ config.websiteUrl = websiteUrl;
314
+ }
187
315
  if (appNameInput) {
188
316
  config.appName = appNameInput;
189
317
  }
190
318
  const strataJsonPath = path.join(process.cwd(), 'strata.json');
191
319
  fs.writeFileSync(strataJsonPath, JSON.stringify(config, null, 2), 'utf8');
192
320
  console.log(`\nConfig file written successfully to: ${strataJsonPath}`);
321
+ if (websiteUrl) {
322
+ try {
323
+ const savedUrl = await saveWebsiteUrl(projectId, websiteUrl, syncToken, config.apiBase);
324
+ console.log(`[Strata] Website URL updated: ${savedUrl}`);
325
+ }
326
+ catch (error) {
327
+ console.error(`[Strata] Website URL was not updated: ${error.message || error}`);
328
+ process.exitCode = 1;
329
+ }
330
+ }
193
331
  console.log('To fetch your design tokens, run: npx strata run');
194
332
  }
333
+ async function runSetUrl(args) {
334
+ const strataJsonPath = path.join(process.cwd(), 'strata.json');
335
+ if (!fs.existsSync(strataJsonPath)) {
336
+ throw new Error('strata.json not found. Run "npx strata init" first.');
337
+ }
338
+ let config;
339
+ try {
340
+ config = JSON.parse(fs.readFileSync(strataJsonPath, 'utf8'));
341
+ }
342
+ catch (error) {
343
+ throw new Error(`Error reading strata.json: ${error.message}`);
344
+ }
345
+ if (!config.projectId)
346
+ throw new Error('The projectId is missing from strata.json.');
347
+ const input = args[2] || await askQuestion('Enter your website URL: ');
348
+ const websiteUrl = normalizeWebsiteUrl(input);
349
+ if (!websiteUrl)
350
+ throw new Error('Enter a public website hostname or HTTP(S) URL.');
351
+ const savedProjectType = config.projectType === 'public' || config.projectType === 'private'
352
+ ? config.projectType
353
+ : undefined;
354
+ const projectType = await askProjectPrivacy(savedProjectType);
355
+ const syncToken = projectType === 'private' ? await getSyncToken(config) : undefined;
356
+ const savedUrl = await saveWebsiteUrl(config.projectId, websiteUrl, syncToken, config.apiBase);
357
+ config.projectType = projectType;
358
+ fs.writeFileSync(strataJsonPath, JSON.stringify(config, null, 2), 'utf8');
359
+ console.log(`[Strata] Website URL updated: ${savedUrl}`);
360
+ }
195
361
  async function runRetrieve(command) {
196
362
  const args = process.argv.slice(2);
197
363
  const getArgValue = (flag) => {
@@ -263,6 +429,15 @@ async function main() {
263
429
  if (command === 'init') {
264
430
  await runInit();
265
431
  }
432
+ else if (command === 'set' && args[1] === 'url') {
433
+ try {
434
+ await runSetUrl(args);
435
+ }
436
+ catch (error) {
437
+ console.error(`[Strata] ${error.message || error}`);
438
+ process.exitCode = 1;
439
+ }
440
+ }
266
441
  else if (command === 'run' || command === 'snapshot') {
267
442
  await runRetrieve(command);
268
443
  }
package/dist/index.esm.js CHANGED
@@ -1166,7 +1166,7 @@ var HeartbeatReporter = /*#__PURE__*/function () {
1166
1166
  if (this.config.syncToken) {
1167
1167
  headers["Authorization"] = "Bearer ".concat(this.config.syncToken);
1168
1168
  }
1169
- version = "2.2.1" ;
1169
+ version = "2.2.2" ;
1170
1170
  body = {
1171
1171
  origin: window.location.origin,
1172
1172
  sdkVersion: version,
package/dist/index.js CHANGED
@@ -1168,7 +1168,7 @@ var HeartbeatReporter = /*#__PURE__*/function () {
1168
1168
  if (this.config.syncToken) {
1169
1169
  headers["Authorization"] = "Bearer ".concat(this.config.syncToken);
1170
1170
  }
1171
- version = "2.2.1" ;
1171
+ version = "2.2.2" ;
1172
1172
  body = {
1173
1173
  origin: window.location.origin,
1174
1174
  sdkVersion: version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strata-ds/core",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "type": "module",
5
5
  "description": "The runtime component library for Strata — design once, sync everywhere.",
6
6
  "homepage": "https://strata.charisol.io",