@strata-ds/core 2.1.6 → 2.1.17
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 +57 -0
- package/dist/cli/snapshot.d.ts +1 -0
- package/dist/cli/snapshot.js +133 -0
- package/dist/data/mock.types.d.ts +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.esm.js +167 -50
- package/dist/index.js +167 -50
- package/dist/providers/StrataProvider.d.ts +12 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -526,6 +526,63 @@ const MyComponent = StrataComponentFactory.createComponent(
|
|
|
526
526
|
);
|
|
527
527
|
```
|
|
528
528
|
|
|
529
|
+
## Offline Resilience & Build-Time Fallbacks
|
|
530
|
+
|
|
531
|
+
`@strata-ds/core` is built to be completely resilient to network outages, CDN downtime, or slow connections using a multi-layered fallback strategy.
|
|
532
|
+
|
|
533
|
+
### How It Works (The 4 Layers of Defense)
|
|
534
|
+
|
|
535
|
+
1. **Static CSS Fallbacks (No-JS / Instant Load)**: Design tokens are pre-rendered into a plain CSS file (`strata-fallbacks.css`) containing direct `:root` custom property assignments. This stylesheet loads statically before React boots, ensuring the page never flickers or appears unstyled.
|
|
536
|
+
2. **Instant React Hydration (`initialData`)**: When the React application mounts, `StrataProvider` initializes with the local snapshot JSON file, allowing it to render immediately with real values without waiting for a CDN fetch.
|
|
537
|
+
3. **Automatic `localStorage` Cache (Fallback)**: If the app is online, the SDK automatically caches successful live sync updates to `localStorage`. If a user reloads the page while offline, the SDK retrieves the last-known-good cache.
|
|
538
|
+
4. **ETag Delta Sync (Bandwidth Optimization)**: Live sync requests include an `If-None-Match` conditional header. If nothing changed, the CDN returns a `304 Not Modified` with no body payload—minimizing data usage to ~0 bytes per poll.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
### Step-by-Step Setup Guide
|
|
543
|
+
|
|
544
|
+
Any project using the Strata Design System can enable full offline resilience in three quick steps:
|
|
545
|
+
|
|
546
|
+
#### Step 1: Generate the Local Snapshot and Fallback CSS
|
|
547
|
+
Run the CLI snapshot tool locally or add it to your project's build process:
|
|
548
|
+
```bash
|
|
549
|
+
npx strata-ds snapshot \
|
|
550
|
+
--project-id "YOUR_PROJECT_ID" \
|
|
551
|
+
--cdn-base "https://your-cdn-base-url.cloudfront.net" \
|
|
552
|
+
--output "./src/data/strata-snapshot.json" \
|
|
553
|
+
--output-css "./src/styles/strata-fallbacks.css" \
|
|
554
|
+
--variables-only
|
|
555
|
+
```
|
|
556
|
+
*(Commit the generated JSON and CSS files to your Git repository as your baseline fallback).*
|
|
557
|
+
|
|
558
|
+
#### Step 2: Import Fallback Styles
|
|
559
|
+
Reference the CSS file in your main style entry point (e.g., `src/index.css` or `src/App.css`):
|
|
560
|
+
```css
|
|
561
|
+
@import './styles/strata-fallbacks.css';
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
#### Step 3: Configure StrataProvider
|
|
565
|
+
Import the snapshot and configure your `StrataProvider`:
|
|
566
|
+
```tsx
|
|
567
|
+
import React from 'react';
|
|
568
|
+
import { StrataProvider } from '@strata-ds/core';
|
|
569
|
+
import strataSnapshot from './data/strata-snapshot.json';
|
|
570
|
+
|
|
571
|
+
export default function App() {
|
|
572
|
+
return (
|
|
573
|
+
<StrataProvider
|
|
574
|
+
initialData={strataSnapshot}
|
|
575
|
+
syncEnabled={true}
|
|
576
|
+
projectId="YOUR_PROJECT_ID"
|
|
577
|
+
snapshotCdnBase="https://your-cdn-base-url.cloudfront.net"
|
|
578
|
+
syncInterval={5000}
|
|
579
|
+
>
|
|
580
|
+
<MainLayout />
|
|
581
|
+
</StrataProvider>
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
529
586
|
## Key Features
|
|
530
587
|
|
|
531
588
|
- 🔁 Live sync with Strata backend
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
function printHelp() {
|
|
7
|
+
console.log(`
|
|
8
|
+
Strata Design System Snapshot CLI
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
npx strata-ds [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--project-id Strata Project ID (required)
|
|
15
|
+
--cdn-base CDN base URL (e.g. https://dx2nhw7wqn4nn.cloudfront.net) (required)
|
|
16
|
+
--output Path to write the snapshot JSON file (optional)
|
|
17
|
+
--output-css Path to write the CSS fallback file (optional)
|
|
18
|
+
--sync-token Authorization Sync Token (optional)
|
|
19
|
+
--variables-only Only output variables in the snapshot JSON (optional)
|
|
20
|
+
--help, -h Show help
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
async function run() {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
26
|
+
printHelp();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
const getArgValue = (flag) => {
|
|
30
|
+
const idx = args.indexOf(flag);
|
|
31
|
+
if (idx !== -1 && idx + 1 < args.length) {
|
|
32
|
+
return args[idx + 1];
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
const hasFlag = (flag) => {
|
|
37
|
+
return args.includes(flag);
|
|
38
|
+
};
|
|
39
|
+
const projectId = getArgValue('--project-id');
|
|
40
|
+
const cdnBase = getArgValue('--cdn-base');
|
|
41
|
+
const outputPath = getArgValue('--output');
|
|
42
|
+
const outputCssPath = getArgValue('--output-css');
|
|
43
|
+
const syncToken = getArgValue('--sync-token');
|
|
44
|
+
const variablesOnly = hasFlag('--variables-only');
|
|
45
|
+
if (!projectId || !cdnBase) {
|
|
46
|
+
console.error('❌ Error: --project-id and --cdn-base are required.');
|
|
47
|
+
printHelp();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const base = cdnBase.replace(/\/+$/, '');
|
|
51
|
+
const snapshotUrl = base.endsWith('/snapshots')
|
|
52
|
+
? `${base}/${projectId}/strata.json`
|
|
53
|
+
: `${base}/snapshots/${projectId}/strata.json`;
|
|
54
|
+
console.log(`📡 Fetching snapshot from: ${snapshotUrl}`);
|
|
55
|
+
try {
|
|
56
|
+
const headers = {};
|
|
57
|
+
if (syncToken) {
|
|
58
|
+
headers['Authorization'] = `Bearer ${syncToken}`;
|
|
59
|
+
}
|
|
60
|
+
const res = await fetch(snapshotUrl, { headers });
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
throw new Error(`Server returned ${res.status} ${res.statusText}`);
|
|
63
|
+
}
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
// Validate shape
|
|
66
|
+
if (!data || typeof data !== 'object' || typeof data.variables !== 'object') {
|
|
67
|
+
throw new Error('Invalid snapshot response shape: missing variables');
|
|
68
|
+
}
|
|
69
|
+
let snapshotJson = data;
|
|
70
|
+
if (variablesOnly) {
|
|
71
|
+
snapshotJson = {
|
|
72
|
+
variables: data.variables,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Write JSON output if requested
|
|
76
|
+
if (outputPath) {
|
|
77
|
+
const resolvedOutput = path.resolve(outputPath);
|
|
78
|
+
fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
|
|
79
|
+
fs.writeFileSync(resolvedOutput, JSON.stringify(snapshotJson, null, 2), 'utf-8');
|
|
80
|
+
console.log(`✅ Snapshot JSON written to: ${resolvedOutput}`);
|
|
81
|
+
}
|
|
82
|
+
// Write CSS output if requested
|
|
83
|
+
if (outputCssPath) {
|
|
84
|
+
const resolvedCssOutput = path.resolve(outputCssPath);
|
|
85
|
+
fs.mkdirSync(path.dirname(resolvedCssOutput), { recursive: true });
|
|
86
|
+
let cssContent = `/* @strata-ds/core — Auto-generated CSS fallback variables\n`;
|
|
87
|
+
cssContent += ` * Project: ${projectId}\n`;
|
|
88
|
+
cssContent += ` * Generated: ${new Date().toISOString()}\n`;
|
|
89
|
+
cssContent += ` * DO NOT EDIT MANUALLY — regenerate with CLI snapshot tool\n`;
|
|
90
|
+
cssContent += ` */\n\n`;
|
|
91
|
+
cssContent += `:root {\n`;
|
|
92
|
+
const variables = data.variables || {};
|
|
93
|
+
for (const [key, val] of Object.entries(variables)) {
|
|
94
|
+
const resolvedVal = val.resolvedValue ?? val.value;
|
|
95
|
+
cssContent += ` ${key}: ${resolvedVal};\n`;
|
|
96
|
+
}
|
|
97
|
+
cssContent += `}\n`;
|
|
98
|
+
fs.writeFileSync(resolvedCssOutput, cssContent, 'utf-8');
|
|
99
|
+
console.log(`✅ CSS fallback written to: ${resolvedCssOutput}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.warn(`⚠️ Warning: Fetch failed: ${error.message || error}`);
|
|
104
|
+
// If fetch failed, check if output files already exist to degrade gracefully (exit 0)
|
|
105
|
+
let jsonExists = false;
|
|
106
|
+
let cssExists = false;
|
|
107
|
+
if (outputPath && fs.existsSync(path.resolve(outputPath))) {
|
|
108
|
+
jsonExists = true;
|
|
109
|
+
}
|
|
110
|
+
if (outputCssPath && fs.existsSync(path.resolve(outputCssPath))) {
|
|
111
|
+
cssExists = true;
|
|
112
|
+
}
|
|
113
|
+
const requestedOutputs = [];
|
|
114
|
+
if (outputPath)
|
|
115
|
+
requestedOutputs.push('JSON');
|
|
116
|
+
if (outputCssPath)
|
|
117
|
+
requestedOutputs.push('CSS');
|
|
118
|
+
const existingOutputs = [];
|
|
119
|
+
if (jsonExists)
|
|
120
|
+
existingOutputs.push('JSON');
|
|
121
|
+
if (cssExists)
|
|
122
|
+
existingOutputs.push('CSS');
|
|
123
|
+
if (requestedOutputs.length > 0 && requestedOutputs.length === existingOutputs.length) {
|
|
124
|
+
console.warn(`ℹ️ Existing output file(s) found. Graceful degradation: exiting 0.`);
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.error('❌ Error: Fetch failed and no existing local outputs found to fall back on.');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
run();
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ type DesignToken = {
|
|
|
11
11
|
comment: string;
|
|
12
12
|
category?: string;
|
|
13
13
|
deprecated?: boolean;
|
|
14
|
+
resolvedValue?: string;
|
|
14
15
|
};
|
|
15
16
|
type ComponentProperty = {
|
|
16
17
|
value: string;
|
|
@@ -87,6 +88,8 @@ interface StrataContextType {
|
|
|
87
88
|
syncToken: string | undefined;
|
|
88
89
|
snapshotCdnBase: string | undefined;
|
|
89
90
|
projectId: string | undefined;
|
|
91
|
+
dataSource: "live" | "cache" | "initial" | "empty";
|
|
92
|
+
cacheAge: number | null;
|
|
90
93
|
}
|
|
91
94
|
declare const useStrata: () => StrataContextType;
|
|
92
95
|
interface StrataProviderProps {
|
|
@@ -133,6 +136,16 @@ interface StrataProviderProps {
|
|
|
133
136
|
* Use this to show a loading indicator or log sync lifecycle events.
|
|
134
137
|
*/
|
|
135
138
|
onSyncStart?: () => void;
|
|
139
|
+
/**
|
|
140
|
+
* Whether to persist and restore cached snapshot to/from localStorage.
|
|
141
|
+
* Defaults to true.
|
|
142
|
+
*/
|
|
143
|
+
persistCache?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Whether to automatically inject design system variables into document.documentElement.
|
|
146
|
+
* Defaults to true.
|
|
147
|
+
*/
|
|
148
|
+
injectCssVars?: boolean;
|
|
136
149
|
}
|
|
137
150
|
declare const StrataProvider: React__default.FC<StrataProviderProps>;
|
|
138
151
|
|
package/dist/index.esm.js
CHANGED
|
@@ -1113,35 +1113,103 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1113
1113
|
projectId = _ref.projectId,
|
|
1114
1114
|
onSyncError = _ref.onSyncError,
|
|
1115
1115
|
onSyncSuccess = _ref.onSyncSuccess,
|
|
1116
|
-
onSyncStart = _ref.onSyncStart
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1116
|
+
onSyncStart = _ref.onSyncStart,
|
|
1117
|
+
_ref$persistCache = _ref.persistCache,
|
|
1118
|
+
persistCache = _ref$persistCache === void 0 ? true : _ref$persistCache,
|
|
1119
|
+
_ref$injectCssVars = _ref.injectCssVars,
|
|
1120
|
+
injectCssVars = _ref$injectCssVars === void 0 ? true : _ref$injectCssVars;
|
|
1121
|
+
var _useState = useState(function () {
|
|
1122
|
+
if (userDesignSystem) return "initial";
|
|
1123
|
+
if (initialData) return "initial";
|
|
1124
|
+
if (persistCache && projectId) {
|
|
1125
|
+
if (typeof window !== "undefined") {
|
|
1126
|
+
try {
|
|
1127
|
+
var cached = localStorage.getItem("strata-cache-".concat(projectId));
|
|
1128
|
+
if (cached) return "cache";
|
|
1129
|
+
} catch (e) {}
|
|
1130
|
+
}
|
|
1123
1131
|
}
|
|
1132
|
+
return "empty";
|
|
1124
1133
|
}),
|
|
1125
1134
|
_useState2 = _slicedToArray(_useState, 2),
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
var _useState3 = useState(
|
|
1135
|
+
dataSource = _useState2[0],
|
|
1136
|
+
setDataSource = _useState2[1];
|
|
1137
|
+
var _useState3 = useState(function () {
|
|
1138
|
+
if (userDesignSystem || initialData) return Date.now();
|
|
1139
|
+
if (persistCache && projectId) {
|
|
1140
|
+
if (typeof window !== "undefined") {
|
|
1141
|
+
try {
|
|
1142
|
+
var ts = localStorage.getItem("strata-cache-ts-".concat(projectId));
|
|
1143
|
+
if (ts) {
|
|
1144
|
+
var parsed = Number.parseInt(ts, 10);
|
|
1145
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1146
|
+
}
|
|
1147
|
+
} catch (e) {}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return null;
|
|
1151
|
+
}),
|
|
1129
1152
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
var _useState5 = useState(
|
|
1153
|
+
dataTimestamp = _useState4[0],
|
|
1154
|
+
setDataTimestamp = _useState4[1];
|
|
1155
|
+
var _useState5 = useState(function () {
|
|
1156
|
+
if (userDesignSystem) return userDesignSystem;
|
|
1157
|
+
if (initialData) return initialData;
|
|
1158
|
+
if (persistCache && projectId) {
|
|
1159
|
+
if (typeof window !== "undefined") {
|
|
1160
|
+
try {
|
|
1161
|
+
var cached = localStorage.getItem("strata-cache-".concat(projectId));
|
|
1162
|
+
if (cached) {
|
|
1163
|
+
var parsed = JSON.parse(cached);
|
|
1164
|
+
if (parsed && parsed.variables && parsed.components) {
|
|
1165
|
+
return parsed;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
} catch (e) {}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return {
|
|
1172
|
+
variables: {},
|
|
1173
|
+
components: {},
|
|
1174
|
+
custom: {
|
|
1175
|
+
variables: {},
|
|
1176
|
+
components: {}
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
}),
|
|
1133
1180
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
var _useState7 = useState(
|
|
1181
|
+
designSystem = _useState6[0],
|
|
1182
|
+
setDesignSystem = _useState6[1];
|
|
1183
|
+
var _useState7 = useState("idle"),
|
|
1137
1184
|
_useState8 = _slicedToArray(_useState7, 2),
|
|
1138
|
-
|
|
1139
|
-
|
|
1185
|
+
syncStatus = _useState8[0],
|
|
1186
|
+
setSyncStatus = _useState8[1];
|
|
1187
|
+
var _useState9 = useState(function () {
|
|
1188
|
+
if (typeof window !== "undefined" && persistCache && projectId) {
|
|
1189
|
+
try {
|
|
1190
|
+
var ts = localStorage.getItem("strata-cache-ts-".concat(projectId));
|
|
1191
|
+
if (ts) {
|
|
1192
|
+
var parsed = Number.parseInt(ts, 10);
|
|
1193
|
+
if (Number.isFinite(parsed)) return new Date(parsed);
|
|
1194
|
+
}
|
|
1195
|
+
} catch (e) {}
|
|
1196
|
+
}
|
|
1197
|
+
return null;
|
|
1198
|
+
}),
|
|
1199
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
1200
|
+
lastSynced = _useState0[0],
|
|
1201
|
+
setLastSynced = _useState0[1];
|
|
1202
|
+
var _useState1 = useState(0),
|
|
1203
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
1204
|
+
syncVersion = _useState10[0],
|
|
1205
|
+
setSyncVersion = _useState10[1];
|
|
1140
1206
|
// Ref that tracks the JSON string of the last successfully synced payload so
|
|
1141
1207
|
// that syncDesignSystem does not need to close over the designSystem state
|
|
1142
1208
|
// value. This keeps the callback stable and prevents the polling interval
|
|
1143
1209
|
// from being continuously reset after each successful sync.
|
|
1144
1210
|
var lastSyncedStringRef = useRef(null);
|
|
1211
|
+
// Ref to track the ETag of the last fetched snapshot
|
|
1212
|
+
var lastETagRef = useRef(null);
|
|
1145
1213
|
// Refs for lifecycle callbacks so that syncDesignSystem does not need to
|
|
1146
1214
|
// close over them. Inline functions passed by the consumer (e.g. anonymous
|
|
1147
1215
|
// arrow functions) would otherwise change identity on every render, forcing
|
|
@@ -1178,7 +1246,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1178
1246
|
}, []);
|
|
1179
1247
|
var syncDesignSystem = useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
1180
1248
|
var _onSyncStartRef$curre;
|
|
1181
|
-
var headers, remoteData, res, _onSyncErrorRef$curre, data, _onSyncErrorRef$curre2, _yield$Promise$all, _yield$Promise$all2, variablesRes, componentsRes, customRes, _yield$Promise$all3, _yield$Promise$all4, variables, components, custom, newDataString, _onSyncSuccessRef$cur, _onSyncErrorRef$curre3, _t;
|
|
1249
|
+
var headers, remoteData, res, _onSyncErrorRef$curre, etag, data, _onSyncErrorRef$curre2, _yield$Promise$all, _yield$Promise$all2, variablesRes, componentsRes, customRes, _yield$Promise$all3, _yield$Promise$all4, variables, components, custom, newDataString, _onSyncSuccessRef$cur, _onSyncErrorRef$curre3, _t;
|
|
1182
1250
|
return _regenerator().w(function (_context) {
|
|
1183
1251
|
while (1) switch (_context.p = _context.n) {
|
|
1184
1252
|
case 0:
|
|
@@ -1195,19 +1263,30 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1195
1263
|
Authorization: "Bearer ".concat(syncToken)
|
|
1196
1264
|
} : {};
|
|
1197
1265
|
if (!snapshotUrl) {
|
|
1198
|
-
_context.n =
|
|
1266
|
+
_context.n = 9;
|
|
1199
1267
|
break;
|
|
1200
1268
|
}
|
|
1269
|
+
if (lastETagRef.current) {
|
|
1270
|
+
headers["If-None-Match"] = lastETagRef.current;
|
|
1271
|
+
}
|
|
1272
|
+
// Single CDN snapshot fetch — edge-cached, 1 request per poll
|
|
1201
1273
|
_context.n = 3;
|
|
1202
1274
|
return fetch(snapshotUrl, {
|
|
1203
1275
|
headers: headers
|
|
1204
1276
|
});
|
|
1205
1277
|
case 3:
|
|
1206
1278
|
res = _context.v;
|
|
1207
|
-
if (!(
|
|
1279
|
+
if (!(res.status === 304)) {
|
|
1208
1280
|
_context.n = 4;
|
|
1209
1281
|
break;
|
|
1210
1282
|
}
|
|
1283
|
+
setSyncStatus("idle");
|
|
1284
|
+
return _context.a(2);
|
|
1285
|
+
case 4:
|
|
1286
|
+
if (!(!res.ok && res.status >= 400 && res.status < 500)) {
|
|
1287
|
+
_context.n = 5;
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1211
1290
|
setSyncStatus("error");
|
|
1212
1291
|
(_onSyncErrorRef$curre = onSyncErrorRef.current) === null || _onSyncErrorRef$curre === void 0 || _onSyncErrorRef$curre.call(onSyncErrorRef, {
|
|
1213
1292
|
code: res.status,
|
|
@@ -1215,19 +1294,23 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1215
1294
|
source: "cdn"
|
|
1216
1295
|
});
|
|
1217
1296
|
return _context.a(2);
|
|
1218
|
-
case
|
|
1297
|
+
case 5:
|
|
1219
1298
|
if (res.ok) {
|
|
1220
|
-
_context.n =
|
|
1299
|
+
_context.n = 6;
|
|
1221
1300
|
break;
|
|
1222
1301
|
}
|
|
1223
1302
|
throw new Error("Snapshot fetch failed: ".concat(res.status));
|
|
1224
|
-
case 5:
|
|
1225
|
-
_context.n = 6;
|
|
1226
|
-
return res.json();
|
|
1227
1303
|
case 6:
|
|
1304
|
+
etag = res.headers.get("ETag");
|
|
1305
|
+
if (etag) {
|
|
1306
|
+
lastETagRef.current = etag;
|
|
1307
|
+
}
|
|
1308
|
+
_context.n = 7;
|
|
1309
|
+
return res.json();
|
|
1310
|
+
case 7:
|
|
1228
1311
|
data = _context.v;
|
|
1229
1312
|
if (!(!data || _typeof(data) !== "object" || Array.isArray(data) || _typeof(data.variables) !== "object" || data.variables === null || Array.isArray(data.variables) || _typeof(data.components) !== "object" || data.components === null || Array.isArray(data.components))) {
|
|
1230
|
-
_context.n =
|
|
1313
|
+
_context.n = 8;
|
|
1231
1314
|
break;
|
|
1232
1315
|
}
|
|
1233
1316
|
console.warn("[strata] Invalid snapshot shape received from CDN — skipping update.");
|
|
@@ -1238,7 +1321,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1238
1321
|
source: "cdn"
|
|
1239
1322
|
});
|
|
1240
1323
|
return _context.a(2);
|
|
1241
|
-
case
|
|
1324
|
+
case 8:
|
|
1242
1325
|
remoteData = {
|
|
1243
1326
|
variables: data.variables,
|
|
1244
1327
|
components: data.components,
|
|
@@ -1251,10 +1334,10 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1251
1334
|
// ComponentNode shape so that useDynamicComponents and the registry
|
|
1252
1335
|
// always receive consistently-shaped data.
|
|
1253
1336
|
remoteData = normalizeComponents(remoteData);
|
|
1254
|
-
_context.n =
|
|
1337
|
+
_context.n = 15;
|
|
1255
1338
|
break;
|
|
1256
|
-
case
|
|
1257
|
-
_context.n =
|
|
1339
|
+
case 9:
|
|
1340
|
+
_context.n = 10;
|
|
1258
1341
|
return Promise.all([fetch("".concat(syncUrl, "/variables"), {
|
|
1259
1342
|
headers: headers
|
|
1260
1343
|
}), fetch("".concat(syncUrl, "/components"), {
|
|
@@ -1262,33 +1345,33 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1262
1345
|
}), fetch("".concat(syncUrl, "/custom"), {
|
|
1263
1346
|
headers: headers
|
|
1264
1347
|
})]);
|
|
1265
|
-
case
|
|
1348
|
+
case 10:
|
|
1266
1349
|
_yield$Promise$all = _context.v;
|
|
1267
1350
|
_yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 3);
|
|
1268
1351
|
variablesRes = _yield$Promise$all2[0];
|
|
1269
1352
|
componentsRes = _yield$Promise$all2[1];
|
|
1270
1353
|
customRes = _yield$Promise$all2[2];
|
|
1271
1354
|
if (variablesRes.ok) {
|
|
1272
|
-
_context.n =
|
|
1355
|
+
_context.n = 11;
|
|
1273
1356
|
break;
|
|
1274
1357
|
}
|
|
1275
1358
|
throw new Error("Variables fetch failed: ".concat(variablesRes.status));
|
|
1276
|
-
case
|
|
1359
|
+
case 11:
|
|
1277
1360
|
if (componentsRes.ok) {
|
|
1278
|
-
_context.n =
|
|
1361
|
+
_context.n = 12;
|
|
1279
1362
|
break;
|
|
1280
1363
|
}
|
|
1281
1364
|
throw new Error("Components fetch failed: ".concat(componentsRes.status));
|
|
1282
|
-
case
|
|
1365
|
+
case 12:
|
|
1283
1366
|
if (customRes.ok) {
|
|
1284
|
-
_context.n =
|
|
1367
|
+
_context.n = 13;
|
|
1285
1368
|
break;
|
|
1286
1369
|
}
|
|
1287
1370
|
throw new Error("Custom fetch failed: ".concat(customRes.status));
|
|
1288
|
-
case 12:
|
|
1289
|
-
_context.n = 13;
|
|
1290
|
-
return Promise.all([variablesRes.json(), componentsRes.json(), customRes.json()]);
|
|
1291
1371
|
case 13:
|
|
1372
|
+
_context.n = 14;
|
|
1373
|
+
return Promise.all([variablesRes.json(), componentsRes.json(), customRes.json()]);
|
|
1374
|
+
case 14:
|
|
1292
1375
|
_yield$Promise$all3 = _context.v;
|
|
1293
1376
|
_yield$Promise$all4 = _slicedToArray(_yield$Promise$all3, 3);
|
|
1294
1377
|
variables = _yield$Promise$all4[0];
|
|
@@ -1306,7 +1389,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1306
1389
|
// ComponentNode shape so that useDynamicComponents and the registry
|
|
1307
1390
|
// always receive consistently-shaped data.
|
|
1308
1391
|
remoteData = normalizeComponents(remoteData);
|
|
1309
|
-
case
|
|
1392
|
+
case 15:
|
|
1310
1393
|
// Only update if data actually changed (compared against the last synced
|
|
1311
1394
|
// snapshot via ref, so this callback stays stable across renders).
|
|
1312
1395
|
newDataString = JSON.stringify(remoteData);
|
|
@@ -1319,14 +1402,22 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1319
1402
|
});
|
|
1320
1403
|
setSyncStatus("success");
|
|
1321
1404
|
setLastSynced(new Date());
|
|
1405
|
+
setDataSource("live");
|
|
1406
|
+
setDataTimestamp(Date.now());
|
|
1407
|
+
if (persistCache && projectId) {
|
|
1408
|
+
try {
|
|
1409
|
+
localStorage.setItem("strata-cache-".concat(projectId), newDataString);
|
|
1410
|
+
localStorage.setItem("strata-cache-ts-".concat(projectId), Date.now().toString());
|
|
1411
|
+
} catch (e) {}
|
|
1412
|
+
}
|
|
1322
1413
|
(_onSyncSuccessRef$cur = onSyncSuccessRef.current) === null || _onSyncSuccessRef$cur === void 0 || _onSyncSuccessRef$cur.call(onSyncSuccessRef, remoteData);
|
|
1323
1414
|
} else {
|
|
1324
1415
|
setSyncStatus("idle");
|
|
1325
1416
|
}
|
|
1326
|
-
_context.n =
|
|
1417
|
+
_context.n = 17;
|
|
1327
1418
|
break;
|
|
1328
|
-
case
|
|
1329
|
-
_context.p =
|
|
1419
|
+
case 16:
|
|
1420
|
+
_context.p = 16;
|
|
1330
1421
|
_t = _context.v;
|
|
1331
1422
|
console.error("❌ Sync failed:", _t);
|
|
1332
1423
|
setSyncStatus("error");
|
|
@@ -1335,11 +1426,11 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1335
1426
|
message: _t instanceof Error ? _t.message : String(_t),
|
|
1336
1427
|
source: snapshotUrl ? "cdn" : "sync"
|
|
1337
1428
|
});
|
|
1338
|
-
case
|
|
1429
|
+
case 17:
|
|
1339
1430
|
return _context.a(2);
|
|
1340
1431
|
}
|
|
1341
|
-
}, _callee, null, [[2,
|
|
1342
|
-
})), [syncEnabled, syncUrl, syncToken, snapshotUrl, normalizeComponents]);
|
|
1432
|
+
}, _callee, null, [[2, 16]]);
|
|
1433
|
+
})), [syncEnabled, syncUrl, syncToken, snapshotUrl, normalizeComponents, persistCache, projectId]);
|
|
1343
1434
|
// Force sync function for manual triggering
|
|
1344
1435
|
var forceSync = useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1345
1436
|
return _regenerator().w(function (_context2) {
|
|
@@ -1428,13 +1519,37 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1428
1519
|
xl: ((_designSystem$variabl24 = designSystem.variables["--shadow-xl"]) === null || _designSystem$variabl24 === void 0 ? void 0 : _designSystem$variabl24.value) || "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
|
|
1429
1520
|
}
|
|
1430
1521
|
});
|
|
1522
|
+
// CSS Variable Auto-Injection
|
|
1523
|
+
useEffect(function () {
|
|
1524
|
+
if (injectCssVars === false) return;
|
|
1525
|
+
if (typeof document === 'undefined') return;
|
|
1526
|
+
var vars = designSystem.variables;
|
|
1527
|
+
if (!vars || Object.keys(vars).length === 0) return;
|
|
1528
|
+
var root = document.documentElement;
|
|
1529
|
+
for (var _i = 0, _Object$entries = Object.entries(vars); _i < _Object$entries.length; _i++) {
|
|
1530
|
+
var _token$resolvedValue;
|
|
1531
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
1532
|
+
key = _Object$entries$_i[0],
|
|
1533
|
+
token = _Object$entries$_i[1];
|
|
1534
|
+
root.style.setProperty(key, (_token$resolvedValue = token.resolvedValue) !== null && _token$resolvedValue !== void 0 ? _token$resolvedValue : token.value);
|
|
1535
|
+
}
|
|
1536
|
+
}, [designSystem.variables, injectCssVars]);
|
|
1431
1537
|
var updateDesignSystem = function updateDesignSystem(newData) {
|
|
1432
1538
|
setDesignSystem(newData);
|
|
1433
1539
|
setSyncVersion(function (prev) {
|
|
1434
1540
|
return prev + 1;
|
|
1435
1541
|
});
|
|
1436
|
-
|
|
1437
|
-
|
|
1542
|
+
setDataSource("initial");
|
|
1543
|
+
setDataTimestamp(Date.now());
|
|
1544
|
+
if (persistCache && projectId) {
|
|
1545
|
+
try {
|
|
1546
|
+
localStorage.setItem("strata-cache-".concat(projectId), JSON.stringify(newData));
|
|
1547
|
+
localStorage.setItem("strata-cache-ts-".concat(projectId), Date.now().toString());
|
|
1548
|
+
} catch (e) {}
|
|
1549
|
+
} else if (!syncEnabled && typeof window !== "undefined") {
|
|
1550
|
+
try {
|
|
1551
|
+
localStorage.setItem("designSystemData", JSON.stringify(newData));
|
|
1552
|
+
} catch (e) {}
|
|
1438
1553
|
}
|
|
1439
1554
|
};
|
|
1440
1555
|
var contextValue = {
|
|
@@ -1449,7 +1564,9 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1449
1564
|
syncInterval: syncInterval,
|
|
1450
1565
|
syncToken: syncToken,
|
|
1451
1566
|
snapshotCdnBase: snapshotCdnBase,
|
|
1452
|
-
projectId: projectId
|
|
1567
|
+
projectId: projectId,
|
|
1568
|
+
dataSource: dataSource,
|
|
1569
|
+
cacheAge: dataTimestamp ? Date.now() - dataTimestamp : null
|
|
1453
1570
|
};
|
|
1454
1571
|
return jsx(StrataContext.Provider, {
|
|
1455
1572
|
value: contextValue,
|
package/dist/index.js
CHANGED
|
@@ -1115,35 +1115,103 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1115
1115
|
projectId = _ref.projectId,
|
|
1116
1116
|
onSyncError = _ref.onSyncError,
|
|
1117
1117
|
onSyncSuccess = _ref.onSyncSuccess,
|
|
1118
|
-
onSyncStart = _ref.onSyncStart
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1118
|
+
onSyncStart = _ref.onSyncStart,
|
|
1119
|
+
_ref$persistCache = _ref.persistCache,
|
|
1120
|
+
persistCache = _ref$persistCache === void 0 ? true : _ref$persistCache,
|
|
1121
|
+
_ref$injectCssVars = _ref.injectCssVars,
|
|
1122
|
+
injectCssVars = _ref$injectCssVars === void 0 ? true : _ref$injectCssVars;
|
|
1123
|
+
var _useState = React.useState(function () {
|
|
1124
|
+
if (userDesignSystem) return "initial";
|
|
1125
|
+
if (initialData) return "initial";
|
|
1126
|
+
if (persistCache && projectId) {
|
|
1127
|
+
if (typeof window !== "undefined") {
|
|
1128
|
+
try {
|
|
1129
|
+
var cached = localStorage.getItem("strata-cache-".concat(projectId));
|
|
1130
|
+
if (cached) return "cache";
|
|
1131
|
+
} catch (e) {}
|
|
1132
|
+
}
|
|
1125
1133
|
}
|
|
1134
|
+
return "empty";
|
|
1126
1135
|
}),
|
|
1127
1136
|
_useState2 = _slicedToArray(_useState, 2),
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
var _useState3 = React.useState(
|
|
1137
|
+
dataSource = _useState2[0],
|
|
1138
|
+
setDataSource = _useState2[1];
|
|
1139
|
+
var _useState3 = React.useState(function () {
|
|
1140
|
+
if (userDesignSystem || initialData) return Date.now();
|
|
1141
|
+
if (persistCache && projectId) {
|
|
1142
|
+
if (typeof window !== "undefined") {
|
|
1143
|
+
try {
|
|
1144
|
+
var ts = localStorage.getItem("strata-cache-ts-".concat(projectId));
|
|
1145
|
+
if (ts) {
|
|
1146
|
+
var parsed = Number.parseInt(ts, 10);
|
|
1147
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1148
|
+
}
|
|
1149
|
+
} catch (e) {}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return null;
|
|
1153
|
+
}),
|
|
1131
1154
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
var _useState5 = React.useState(
|
|
1155
|
+
dataTimestamp = _useState4[0],
|
|
1156
|
+
setDataTimestamp = _useState4[1];
|
|
1157
|
+
var _useState5 = React.useState(function () {
|
|
1158
|
+
if (userDesignSystem) return userDesignSystem;
|
|
1159
|
+
if (initialData) return initialData;
|
|
1160
|
+
if (persistCache && projectId) {
|
|
1161
|
+
if (typeof window !== "undefined") {
|
|
1162
|
+
try {
|
|
1163
|
+
var cached = localStorage.getItem("strata-cache-".concat(projectId));
|
|
1164
|
+
if (cached) {
|
|
1165
|
+
var parsed = JSON.parse(cached);
|
|
1166
|
+
if (parsed && parsed.variables && parsed.components) {
|
|
1167
|
+
return parsed;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
} catch (e) {}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return {
|
|
1174
|
+
variables: {},
|
|
1175
|
+
components: {},
|
|
1176
|
+
custom: {
|
|
1177
|
+
variables: {},
|
|
1178
|
+
components: {}
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
}),
|
|
1135
1182
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
var _useState7 = React.useState(
|
|
1183
|
+
designSystem = _useState6[0],
|
|
1184
|
+
setDesignSystem = _useState6[1];
|
|
1185
|
+
var _useState7 = React.useState("idle"),
|
|
1139
1186
|
_useState8 = _slicedToArray(_useState7, 2),
|
|
1140
|
-
|
|
1141
|
-
|
|
1187
|
+
syncStatus = _useState8[0],
|
|
1188
|
+
setSyncStatus = _useState8[1];
|
|
1189
|
+
var _useState9 = React.useState(function () {
|
|
1190
|
+
if (typeof window !== "undefined" && persistCache && projectId) {
|
|
1191
|
+
try {
|
|
1192
|
+
var ts = localStorage.getItem("strata-cache-ts-".concat(projectId));
|
|
1193
|
+
if (ts) {
|
|
1194
|
+
var parsed = Number.parseInt(ts, 10);
|
|
1195
|
+
if (Number.isFinite(parsed)) return new Date(parsed);
|
|
1196
|
+
}
|
|
1197
|
+
} catch (e) {}
|
|
1198
|
+
}
|
|
1199
|
+
return null;
|
|
1200
|
+
}),
|
|
1201
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
1202
|
+
lastSynced = _useState0[0],
|
|
1203
|
+
setLastSynced = _useState0[1];
|
|
1204
|
+
var _useState1 = React.useState(0),
|
|
1205
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
1206
|
+
syncVersion = _useState10[0],
|
|
1207
|
+
setSyncVersion = _useState10[1];
|
|
1142
1208
|
// Ref that tracks the JSON string of the last successfully synced payload so
|
|
1143
1209
|
// that syncDesignSystem does not need to close over the designSystem state
|
|
1144
1210
|
// value. This keeps the callback stable and prevents the polling interval
|
|
1145
1211
|
// from being continuously reset after each successful sync.
|
|
1146
1212
|
var lastSyncedStringRef = React.useRef(null);
|
|
1213
|
+
// Ref to track the ETag of the last fetched snapshot
|
|
1214
|
+
var lastETagRef = React.useRef(null);
|
|
1147
1215
|
// Refs for lifecycle callbacks so that syncDesignSystem does not need to
|
|
1148
1216
|
// close over them. Inline functions passed by the consumer (e.g. anonymous
|
|
1149
1217
|
// arrow functions) would otherwise change identity on every render, forcing
|
|
@@ -1180,7 +1248,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1180
1248
|
}, []);
|
|
1181
1249
|
var syncDesignSystem = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
1182
1250
|
var _onSyncStartRef$curre;
|
|
1183
|
-
var headers, remoteData, res, _onSyncErrorRef$curre, data, _onSyncErrorRef$curre2, _yield$Promise$all, _yield$Promise$all2, variablesRes, componentsRes, customRes, _yield$Promise$all3, _yield$Promise$all4, variables, components, custom, newDataString, _onSyncSuccessRef$cur, _onSyncErrorRef$curre3, _t;
|
|
1251
|
+
var headers, remoteData, res, _onSyncErrorRef$curre, etag, data, _onSyncErrorRef$curre2, _yield$Promise$all, _yield$Promise$all2, variablesRes, componentsRes, customRes, _yield$Promise$all3, _yield$Promise$all4, variables, components, custom, newDataString, _onSyncSuccessRef$cur, _onSyncErrorRef$curre3, _t;
|
|
1184
1252
|
return _regenerator().w(function (_context) {
|
|
1185
1253
|
while (1) switch (_context.p = _context.n) {
|
|
1186
1254
|
case 0:
|
|
@@ -1197,19 +1265,30 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1197
1265
|
Authorization: "Bearer ".concat(syncToken)
|
|
1198
1266
|
} : {};
|
|
1199
1267
|
if (!snapshotUrl) {
|
|
1200
|
-
_context.n =
|
|
1268
|
+
_context.n = 9;
|
|
1201
1269
|
break;
|
|
1202
1270
|
}
|
|
1271
|
+
if (lastETagRef.current) {
|
|
1272
|
+
headers["If-None-Match"] = lastETagRef.current;
|
|
1273
|
+
}
|
|
1274
|
+
// Single CDN snapshot fetch — edge-cached, 1 request per poll
|
|
1203
1275
|
_context.n = 3;
|
|
1204
1276
|
return fetch(snapshotUrl, {
|
|
1205
1277
|
headers: headers
|
|
1206
1278
|
});
|
|
1207
1279
|
case 3:
|
|
1208
1280
|
res = _context.v;
|
|
1209
|
-
if (!(
|
|
1281
|
+
if (!(res.status === 304)) {
|
|
1210
1282
|
_context.n = 4;
|
|
1211
1283
|
break;
|
|
1212
1284
|
}
|
|
1285
|
+
setSyncStatus("idle");
|
|
1286
|
+
return _context.a(2);
|
|
1287
|
+
case 4:
|
|
1288
|
+
if (!(!res.ok && res.status >= 400 && res.status < 500)) {
|
|
1289
|
+
_context.n = 5;
|
|
1290
|
+
break;
|
|
1291
|
+
}
|
|
1213
1292
|
setSyncStatus("error");
|
|
1214
1293
|
(_onSyncErrorRef$curre = onSyncErrorRef.current) === null || _onSyncErrorRef$curre === void 0 || _onSyncErrorRef$curre.call(onSyncErrorRef, {
|
|
1215
1294
|
code: res.status,
|
|
@@ -1217,19 +1296,23 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1217
1296
|
source: "cdn"
|
|
1218
1297
|
});
|
|
1219
1298
|
return _context.a(2);
|
|
1220
|
-
case
|
|
1299
|
+
case 5:
|
|
1221
1300
|
if (res.ok) {
|
|
1222
|
-
_context.n =
|
|
1301
|
+
_context.n = 6;
|
|
1223
1302
|
break;
|
|
1224
1303
|
}
|
|
1225
1304
|
throw new Error("Snapshot fetch failed: ".concat(res.status));
|
|
1226
|
-
case 5:
|
|
1227
|
-
_context.n = 6;
|
|
1228
|
-
return res.json();
|
|
1229
1305
|
case 6:
|
|
1306
|
+
etag = res.headers.get("ETag");
|
|
1307
|
+
if (etag) {
|
|
1308
|
+
lastETagRef.current = etag;
|
|
1309
|
+
}
|
|
1310
|
+
_context.n = 7;
|
|
1311
|
+
return res.json();
|
|
1312
|
+
case 7:
|
|
1230
1313
|
data = _context.v;
|
|
1231
1314
|
if (!(!data || _typeof(data) !== "object" || Array.isArray(data) || _typeof(data.variables) !== "object" || data.variables === null || Array.isArray(data.variables) || _typeof(data.components) !== "object" || data.components === null || Array.isArray(data.components))) {
|
|
1232
|
-
_context.n =
|
|
1315
|
+
_context.n = 8;
|
|
1233
1316
|
break;
|
|
1234
1317
|
}
|
|
1235
1318
|
console.warn("[strata] Invalid snapshot shape received from CDN — skipping update.");
|
|
@@ -1240,7 +1323,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1240
1323
|
source: "cdn"
|
|
1241
1324
|
});
|
|
1242
1325
|
return _context.a(2);
|
|
1243
|
-
case
|
|
1326
|
+
case 8:
|
|
1244
1327
|
remoteData = {
|
|
1245
1328
|
variables: data.variables,
|
|
1246
1329
|
components: data.components,
|
|
@@ -1253,10 +1336,10 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1253
1336
|
// ComponentNode shape so that useDynamicComponents and the registry
|
|
1254
1337
|
// always receive consistently-shaped data.
|
|
1255
1338
|
remoteData = normalizeComponents(remoteData);
|
|
1256
|
-
_context.n =
|
|
1339
|
+
_context.n = 15;
|
|
1257
1340
|
break;
|
|
1258
|
-
case
|
|
1259
|
-
_context.n =
|
|
1341
|
+
case 9:
|
|
1342
|
+
_context.n = 10;
|
|
1260
1343
|
return Promise.all([fetch("".concat(syncUrl, "/variables"), {
|
|
1261
1344
|
headers: headers
|
|
1262
1345
|
}), fetch("".concat(syncUrl, "/components"), {
|
|
@@ -1264,33 +1347,33 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1264
1347
|
}), fetch("".concat(syncUrl, "/custom"), {
|
|
1265
1348
|
headers: headers
|
|
1266
1349
|
})]);
|
|
1267
|
-
case
|
|
1350
|
+
case 10:
|
|
1268
1351
|
_yield$Promise$all = _context.v;
|
|
1269
1352
|
_yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 3);
|
|
1270
1353
|
variablesRes = _yield$Promise$all2[0];
|
|
1271
1354
|
componentsRes = _yield$Promise$all2[1];
|
|
1272
1355
|
customRes = _yield$Promise$all2[2];
|
|
1273
1356
|
if (variablesRes.ok) {
|
|
1274
|
-
_context.n =
|
|
1357
|
+
_context.n = 11;
|
|
1275
1358
|
break;
|
|
1276
1359
|
}
|
|
1277
1360
|
throw new Error("Variables fetch failed: ".concat(variablesRes.status));
|
|
1278
|
-
case
|
|
1361
|
+
case 11:
|
|
1279
1362
|
if (componentsRes.ok) {
|
|
1280
|
-
_context.n =
|
|
1363
|
+
_context.n = 12;
|
|
1281
1364
|
break;
|
|
1282
1365
|
}
|
|
1283
1366
|
throw new Error("Components fetch failed: ".concat(componentsRes.status));
|
|
1284
|
-
case
|
|
1367
|
+
case 12:
|
|
1285
1368
|
if (customRes.ok) {
|
|
1286
|
-
_context.n =
|
|
1369
|
+
_context.n = 13;
|
|
1287
1370
|
break;
|
|
1288
1371
|
}
|
|
1289
1372
|
throw new Error("Custom fetch failed: ".concat(customRes.status));
|
|
1290
|
-
case 12:
|
|
1291
|
-
_context.n = 13;
|
|
1292
|
-
return Promise.all([variablesRes.json(), componentsRes.json(), customRes.json()]);
|
|
1293
1373
|
case 13:
|
|
1374
|
+
_context.n = 14;
|
|
1375
|
+
return Promise.all([variablesRes.json(), componentsRes.json(), customRes.json()]);
|
|
1376
|
+
case 14:
|
|
1294
1377
|
_yield$Promise$all3 = _context.v;
|
|
1295
1378
|
_yield$Promise$all4 = _slicedToArray(_yield$Promise$all3, 3);
|
|
1296
1379
|
variables = _yield$Promise$all4[0];
|
|
@@ -1308,7 +1391,7 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1308
1391
|
// ComponentNode shape so that useDynamicComponents and the registry
|
|
1309
1392
|
// always receive consistently-shaped data.
|
|
1310
1393
|
remoteData = normalizeComponents(remoteData);
|
|
1311
|
-
case
|
|
1394
|
+
case 15:
|
|
1312
1395
|
// Only update if data actually changed (compared against the last synced
|
|
1313
1396
|
// snapshot via ref, so this callback stays stable across renders).
|
|
1314
1397
|
newDataString = JSON.stringify(remoteData);
|
|
@@ -1321,14 +1404,22 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1321
1404
|
});
|
|
1322
1405
|
setSyncStatus("success");
|
|
1323
1406
|
setLastSynced(new Date());
|
|
1407
|
+
setDataSource("live");
|
|
1408
|
+
setDataTimestamp(Date.now());
|
|
1409
|
+
if (persistCache && projectId) {
|
|
1410
|
+
try {
|
|
1411
|
+
localStorage.setItem("strata-cache-".concat(projectId), newDataString);
|
|
1412
|
+
localStorage.setItem("strata-cache-ts-".concat(projectId), Date.now().toString());
|
|
1413
|
+
} catch (e) {}
|
|
1414
|
+
}
|
|
1324
1415
|
(_onSyncSuccessRef$cur = onSyncSuccessRef.current) === null || _onSyncSuccessRef$cur === void 0 || _onSyncSuccessRef$cur.call(onSyncSuccessRef, remoteData);
|
|
1325
1416
|
} else {
|
|
1326
1417
|
setSyncStatus("idle");
|
|
1327
1418
|
}
|
|
1328
|
-
_context.n =
|
|
1419
|
+
_context.n = 17;
|
|
1329
1420
|
break;
|
|
1330
|
-
case
|
|
1331
|
-
_context.p =
|
|
1421
|
+
case 16:
|
|
1422
|
+
_context.p = 16;
|
|
1332
1423
|
_t = _context.v;
|
|
1333
1424
|
console.error("❌ Sync failed:", _t);
|
|
1334
1425
|
setSyncStatus("error");
|
|
@@ -1337,11 +1428,11 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1337
1428
|
message: _t instanceof Error ? _t.message : String(_t),
|
|
1338
1429
|
source: snapshotUrl ? "cdn" : "sync"
|
|
1339
1430
|
});
|
|
1340
|
-
case
|
|
1431
|
+
case 17:
|
|
1341
1432
|
return _context.a(2);
|
|
1342
1433
|
}
|
|
1343
|
-
}, _callee, null, [[2,
|
|
1344
|
-
})), [syncEnabled, syncUrl, syncToken, snapshotUrl, normalizeComponents]);
|
|
1434
|
+
}, _callee, null, [[2, 16]]);
|
|
1435
|
+
})), [syncEnabled, syncUrl, syncToken, snapshotUrl, normalizeComponents, persistCache, projectId]);
|
|
1345
1436
|
// Force sync function for manual triggering
|
|
1346
1437
|
var forceSync = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1347
1438
|
return _regenerator().w(function (_context2) {
|
|
@@ -1430,13 +1521,37 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1430
1521
|
xl: ((_designSystem$variabl24 = designSystem.variables["--shadow-xl"]) === null || _designSystem$variabl24 === void 0 ? void 0 : _designSystem$variabl24.value) || "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
|
|
1431
1522
|
}
|
|
1432
1523
|
});
|
|
1524
|
+
// CSS Variable Auto-Injection
|
|
1525
|
+
React.useEffect(function () {
|
|
1526
|
+
if (injectCssVars === false) return;
|
|
1527
|
+
if (typeof document === 'undefined') return;
|
|
1528
|
+
var vars = designSystem.variables;
|
|
1529
|
+
if (!vars || Object.keys(vars).length === 0) return;
|
|
1530
|
+
var root = document.documentElement;
|
|
1531
|
+
for (var _i = 0, _Object$entries = Object.entries(vars); _i < _Object$entries.length; _i++) {
|
|
1532
|
+
var _token$resolvedValue;
|
|
1533
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
1534
|
+
key = _Object$entries$_i[0],
|
|
1535
|
+
token = _Object$entries$_i[1];
|
|
1536
|
+
root.style.setProperty(key, (_token$resolvedValue = token.resolvedValue) !== null && _token$resolvedValue !== void 0 ? _token$resolvedValue : token.value);
|
|
1537
|
+
}
|
|
1538
|
+
}, [designSystem.variables, injectCssVars]);
|
|
1433
1539
|
var updateDesignSystem = function updateDesignSystem(newData) {
|
|
1434
1540
|
setDesignSystem(newData);
|
|
1435
1541
|
setSyncVersion(function (prev) {
|
|
1436
1542
|
return prev + 1;
|
|
1437
1543
|
});
|
|
1438
|
-
|
|
1439
|
-
|
|
1544
|
+
setDataSource("initial");
|
|
1545
|
+
setDataTimestamp(Date.now());
|
|
1546
|
+
if (persistCache && projectId) {
|
|
1547
|
+
try {
|
|
1548
|
+
localStorage.setItem("strata-cache-".concat(projectId), JSON.stringify(newData));
|
|
1549
|
+
localStorage.setItem("strata-cache-ts-".concat(projectId), Date.now().toString());
|
|
1550
|
+
} catch (e) {}
|
|
1551
|
+
} else if (!syncEnabled && typeof window !== "undefined") {
|
|
1552
|
+
try {
|
|
1553
|
+
localStorage.setItem("designSystemData", JSON.stringify(newData));
|
|
1554
|
+
} catch (e) {}
|
|
1440
1555
|
}
|
|
1441
1556
|
};
|
|
1442
1557
|
var contextValue = {
|
|
@@ -1451,7 +1566,9 @@ var StrataProvider = function StrataProvider(_ref) {
|
|
|
1451
1566
|
syncInterval: syncInterval,
|
|
1452
1567
|
syncToken: syncToken,
|
|
1453
1568
|
snapshotCdnBase: snapshotCdnBase,
|
|
1454
|
-
projectId: projectId
|
|
1569
|
+
projectId: projectId,
|
|
1570
|
+
dataSource: dataSource,
|
|
1571
|
+
cacheAge: dataTimestamp ? Date.now() - dataTimestamp : null
|
|
1455
1572
|
};
|
|
1456
1573
|
return jsxRuntime.jsx(StrataContext.Provider, {
|
|
1457
1574
|
value: contextValue,
|
|
@@ -13,6 +13,8 @@ interface StrataContextType {
|
|
|
13
13
|
syncToken: string | undefined;
|
|
14
14
|
snapshotCdnBase: string | undefined;
|
|
15
15
|
projectId: string | undefined;
|
|
16
|
+
dataSource: "live" | "cache" | "initial" | "empty";
|
|
17
|
+
cacheAge: number | null;
|
|
16
18
|
}
|
|
17
19
|
export declare const useStrata: () => StrataContextType;
|
|
18
20
|
interface StrataProviderProps {
|
|
@@ -59,6 +61,16 @@ interface StrataProviderProps {
|
|
|
59
61
|
* Use this to show a loading indicator or log sync lifecycle events.
|
|
60
62
|
*/
|
|
61
63
|
onSyncStart?: () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Whether to persist and restore cached snapshot to/from localStorage.
|
|
66
|
+
* Defaults to true.
|
|
67
|
+
*/
|
|
68
|
+
persistCache?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Whether to automatically inject design system variables into document.documentElement.
|
|
71
|
+
* Defaults to true.
|
|
72
|
+
*/
|
|
73
|
+
injectCssVars?: boolean;
|
|
62
74
|
}
|
|
63
75
|
export declare const StrataProvider: React.FC<StrataProviderProps>;
|
|
64
76
|
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strata-ds/core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The runtime component library for Strata — design once, sync everywhere.",
|
|
6
6
|
"homepage": "https://strata.charisol.io",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"module": "dist/index.esm.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"strata-ds": "./dist/cli/snapshot.js"
|
|
12
|
+
},
|
|
10
13
|
"exports": {
|
|
11
14
|
".": {
|
|
12
15
|
"import": "./dist/index.esm.js",
|