@strata-ds/core 2.1.21 → 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 +63 -0
- package/dist/cli/postinstall.js +1 -1
- package/dist/cli/snapshot.js +182 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -259,6 +259,9 @@ Wrap your application's root component with `StrataProvider`. This provider mana
|
|
|
259
259
|
| `userDesignSystem` | `DesignSystemData` | Override design system data — useful for storybook, testing, or static mode. Takes precedence over `initialData`. |
|
|
260
260
|
| `onSyncError` | `(error: { code: number; message: string; source: string }) => void` | Callback invoked when a sync cycle fails. `code` is the HTTP status (0 for non-HTTP errors), `message` is a human-readable description, and `source` is `"cdn"` or `"sync"`. |
|
|
261
261
|
| `onSyncSuccess` | `(data: DesignSystemData) => void` | Callback invoked whenever a sync cycle succeeds with new data. |
|
|
262
|
+
| `reportUsage` | `boolean` | Whether to send non-sensitive usage telemetry to power "See It in the Wild" in the dashboard. Defaults to `true` in production. Set to `false` to opt out. |
|
|
263
|
+
| `appName` | `string` | Optional human-readable display name for your app in the Strata dashboard (e.g. `"Storefront Web"`). |
|
|
264
|
+
| `environment` | `string` | Override automatic environment detection (`"production"`, `"staging"`, `"development"`). Heartbeat is paused when set to `"development"`. |
|
|
262
265
|
|
|
263
266
|
```tsx
|
|
264
267
|
// app/layout.tsx (Next.js App Router)
|
|
@@ -307,6 +310,66 @@ Projects with `syncToken` set are only readable by clients that present the corr
|
|
|
307
310
|
</StrataProvider>
|
|
308
311
|
```
|
|
309
312
|
|
|
313
|
+
### Connected Apps Telemetry ("See It in the Wild")
|
|
314
|
+
|
|
315
|
+
By default in production environments, `<StrataProvider>` sends a lightweight telemetry heartbeat every 5 minutes to report active design system surface integrations.
|
|
316
|
+
|
|
317
|
+
#### What is "Connected Apps"?
|
|
318
|
+
"Connected Apps" (featured as "See It in the Wild" on your public project page and Settings dashboard) provides real-time visibility into which live applications are consuming your design system, their active SDK version, environment, and health status (`Live`, `Stale`, or `Dormant`).
|
|
319
|
+
|
|
320
|
+
#### Data Transmitted
|
|
321
|
+
The telemetry heartbeat contains **only** non-sensitive metadata:
|
|
322
|
+
- Application origin / hostname (e.g. `https://app.example.com`)
|
|
323
|
+
- Strata SDK version (e.g. `2.2.0`)
|
|
324
|
+
- Sync mode (`cdn`, `legacy`, `static`) and polling interval
|
|
325
|
+
- User Agent string (browser family)
|
|
326
|
+
- Optional `appName` prop value
|
|
327
|
+
|
|
328
|
+
#### How to Opt Out
|
|
329
|
+
To disable telemetry heartbeats entirely, set `reportUsage={false}`:
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
<StrataProvider
|
|
333
|
+
syncEnabled={true}
|
|
334
|
+
projectId="YOUR_PROJECT_ID"
|
|
335
|
+
snapshotCdnBase="https://cdn.strata.charisol.io"
|
|
336
|
+
reportUsage={false}
|
|
337
|
+
>
|
|
338
|
+
{children}
|
|
339
|
+
</StrataProvider>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
#### CLI Setup & Build-Time Registration
|
|
343
|
+
You can also initialize project configuration using `npx strata init`:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
npx strata init
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This generates a `strata.json` configuration file:
|
|
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
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"projectId": "YOUR_PROJECT_ID",
|
|
364
|
+
"projectType": "public",
|
|
365
|
+
"snapshotCdnBase": "https://cdn.strata.charisol.io",
|
|
366
|
+
"appName": "Storefront Web",
|
|
367
|
+
"reportUsage": true
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
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.
|
|
372
|
+
|
|
310
373
|
## Using Design System Components
|
|
311
374
|
|
|
312
375
|
`@strata-ds/core` provides dynamic access to your components defined in Strata.
|
package/dist/cli/postinstall.js
CHANGED
|
@@ -175,7 +175,7 @@ async function run() {
|
|
|
175
175
|
const headers = { 'Content-Type': 'application/json' };
|
|
176
176
|
if (syncToken)
|
|
177
177
|
headers['Authorization'] = `Bearer ${syncToken}`;
|
|
178
|
-
let sdkVersion = '2.
|
|
178
|
+
let sdkVersion = '2.2.0';
|
|
179
179
|
try {
|
|
180
180
|
const pkgPath = path.resolve(__dirname, '../../package.json');
|
|
181
181
|
if (fs.existsSync(pkgPath)) {
|
package/dist/cli/snapshot.js
CHANGED
|
@@ -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,23 +280,84 @@ 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';
|
|
174
298
|
const varsOnlyInput = await askQuestion('Only output variables in snapshot JSON? (Y/n): ');
|
|
175
299
|
const variablesOnly = varsOnlyInput.toLowerCase() !== 'n';
|
|
300
|
+
const appNameInput = await askQuestion('Enter Application Name (optional, e.g. "Storefront Web"): ');
|
|
301
|
+
const reportUsageInput = await askQuestion('Report usage telemetry to Strata Dashboard? (Y/n): ');
|
|
302
|
+
const reportUsage = reportUsageInput.toLowerCase() !== 'n';
|
|
176
303
|
const config = {
|
|
177
304
|
projectId,
|
|
178
305
|
snapshotCdnBase: cdnBase,
|
|
179
306
|
output,
|
|
180
307
|
outputCss,
|
|
181
308
|
variablesOnly,
|
|
309
|
+
reportUsage,
|
|
310
|
+
projectType,
|
|
182
311
|
};
|
|
312
|
+
if (websiteUrl) {
|
|
313
|
+
config.websiteUrl = websiteUrl;
|
|
314
|
+
}
|
|
315
|
+
if (appNameInput) {
|
|
316
|
+
config.appName = appNameInput;
|
|
317
|
+
}
|
|
183
318
|
const strataJsonPath = path.join(process.cwd(), 'strata.json');
|
|
184
319
|
fs.writeFileSync(strataJsonPath, JSON.stringify(config, null, 2), 'utf8');
|
|
185
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
|
+
}
|
|
186
331
|
console.log('To fetch your design tokens, run: npx strata run');
|
|
187
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
|
+
}
|
|
188
361
|
async function runRetrieve(command) {
|
|
189
362
|
const args = process.argv.slice(2);
|
|
190
363
|
const getArgValue = (flag) => {
|
|
@@ -256,6 +429,15 @@ async function main() {
|
|
|
256
429
|
if (command === 'init') {
|
|
257
430
|
await runInit();
|
|
258
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
|
+
}
|
|
259
441
|
else if (command === 'run' || command === 'snapshot') {
|
|
260
442
|
await runRetrieve(command);
|
|
261
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.
|
|
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.
|
|
1171
|
+
version = "2.2.2" ;
|
|
1172
1172
|
body = {
|
|
1173
1173
|
origin: window.location.origin,
|
|
1174
1174
|
sdkVersion: version,
|