@bravostudioai/react 0.1.32 → 0.1.35
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 +84 -35
- package/dist/cli/commands/download.js +22 -39
- package/dist/cli/commands/download.js.map +1 -1
- package/dist/cli/commands/generate.js +140 -161
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli.js +21 -24
- package/dist/cli.js.map +1 -1
- package/dist/codegen/generator.js +126 -125
- package/dist/codegen/generator.js.map +1 -1
- package/dist/components/EncoreApp.js +157 -133
- package/dist/components/EncoreApp.js.map +1 -1
- package/dist/src/cli/commands/download.d.ts +1 -1
- package/dist/src/cli/commands/download.d.ts.map +1 -1
- package/dist/src/cli/commands/generate.d.ts +1 -1
- package/dist/src/cli/commands/generate.d.ts.map +1 -1
- package/dist/src/codegen/generator.d.ts +1 -1
- package/dist/src/codegen/generator.d.ts.map +1 -1
- package/dist/src/components/EncoreApp.d.ts +3 -1
- package/dist/src/components/EncoreApp.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/commands/download.ts +8 -28
- package/src/cli/commands/generate.ts +44 -45
- package/src/cli/index.ts +49 -32
- package/src/codegen/generator.ts +6 -3
- package/src/components/EncoreApp.tsx +111 -21
- package/src/version.ts +1 -1
|
@@ -76,6 +76,8 @@ export type EncoreAppProps = {
|
|
|
76
76
|
pageDefinition?: any;
|
|
77
77
|
/** Provide component code directly instead of fetching (for offline/bundled deployments) */
|
|
78
78
|
componentCode?: string;
|
|
79
|
+
/** Deployment mode: dynamic (default), optimistic, or production */
|
|
80
|
+
mode?: "dynamic" | "optimistic" | "production";
|
|
79
81
|
};
|
|
80
82
|
|
|
81
83
|
type Props = EncoreAppProps;
|
|
@@ -143,8 +145,9 @@ const EncoreApp = ({
|
|
|
143
145
|
appDefinition,
|
|
144
146
|
pageDefinition,
|
|
145
147
|
componentCode,
|
|
148
|
+
mode,
|
|
146
149
|
}: Props) => {
|
|
147
|
-
logger.debug(
|
|
150
|
+
logger.debug("EncoreApp render", { appId, pageId, mode });
|
|
148
151
|
|
|
149
152
|
// CRITICAL: Set baseURL BEFORE any hooks that might trigger fetches
|
|
150
153
|
// This must happen synchronously, not in useEffect, because useSWR will fetch immediately
|
|
@@ -210,16 +213,38 @@ const EncoreApp = ({
|
|
|
210
213
|
});
|
|
211
214
|
|
|
212
215
|
const useLocalFlag = source === "local" || isLocalMode();
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
|
|
217
|
+
// Determine if we should fetch app definition
|
|
218
|
+
// Production: Do NOT fetch (use appDefinition)
|
|
219
|
+
// Dynamic: Fetch always (ignore appDefinition unless valid) - wait, dynamic assumes no bundled data usually, or if provided, use it? Current logic uses simple check.
|
|
220
|
+
// Optimistic: Fetch always, but use appDefinition as fallback/initial data
|
|
221
|
+
|
|
222
|
+
// Logic:
|
|
223
|
+
// If we have appDefinition:
|
|
224
|
+
// - Production: Use it, no fetch.
|
|
225
|
+
// - Optimistic: Use it as fallbackData (SWR), and fetch.
|
|
226
|
+
// - Dynamic (Edge Case): User provided data but wants dynamic? Treat same as Optimistic roughly, or just ignore data.
|
|
227
|
+
// But usually Dynamic won't have appDefinition passed unless manually done.
|
|
228
|
+
|
|
229
|
+
// Refined Logic based on `mode`:
|
|
230
|
+
const isProductionMode = mode === "production";
|
|
231
|
+
const isOptimisticMode = mode === "optimistic";
|
|
232
|
+
// Default to dynamic if not specified
|
|
233
|
+
const isDynamicMode = !isProductionMode && !isOptimisticMode;
|
|
234
|
+
|
|
235
|
+
const shouldFetchApp = !isProductionMode && appId;
|
|
236
|
+
const appUrl = shouldFetchApp
|
|
237
|
+
? `/devices/apps/${appId}${useLocalFlag ? "?useLocal=1" : ""}`
|
|
238
|
+
: null;
|
|
217
239
|
|
|
218
240
|
const appSWR = useSWR(appUrl, fetcher, {
|
|
219
|
-
suspense: !!appUrl, //
|
|
241
|
+
suspense: isDynamicMode && !!appUrl, // Suspense only for Dynamic mode
|
|
242
|
+
fallbackData: isOptimisticMode && appDefinition ? appDefinition : undefined,
|
|
243
|
+
revalidateOnMount: true, // Ensure we fetch fresh data in optimistic mode
|
|
220
244
|
});
|
|
221
245
|
|
|
222
|
-
const app =
|
|
246
|
+
const app =
|
|
247
|
+
isProductionMode && appDefinition ? { data: appDefinition } : appSWR;
|
|
223
248
|
|
|
224
249
|
useEffect(() => {
|
|
225
250
|
setApp(app.data);
|
|
@@ -258,31 +283,96 @@ const EncoreApp = ({
|
|
|
258
283
|
})();
|
|
259
284
|
}, [assetsById]);
|
|
260
285
|
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
pageId &&
|
|
265
|
-
`/devices/apps/${appId}/node/${pageId}${
|
|
286
|
+
const shouldFetchPage = !isProductionMode && appId && pageId;
|
|
287
|
+
const pageUrl = shouldFetchPage
|
|
288
|
+
? `/devices/apps/${appId}/node/${pageId}${
|
|
266
289
|
useLocalFlag ? "?useLocal=1" : ""
|
|
267
|
-
}
|
|
290
|
+
}`
|
|
291
|
+
: null;
|
|
292
|
+
|
|
293
|
+
logger.debug("Page data fetch", { pageUrl, mode });
|
|
294
|
+
|
|
295
|
+
const pageSWR = useSWR(pageUrl, fetcher, {
|
|
296
|
+
suspense: isDynamicMode && !!pageUrl,
|
|
297
|
+
fallbackData:
|
|
298
|
+
isOptimisticMode && pageDefinition ? pageDefinition : undefined,
|
|
299
|
+
revalidateOnMount: true,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const pageData =
|
|
303
|
+
isProductionMode && pageDefinition ? { data: pageDefinition } : pageSWR;
|
|
304
|
+
|
|
305
|
+
// Specific logic for Component Code fetching in Optimistic Mode
|
|
306
|
+
// If optimistic, we have componentCode passed in (bundled). We use that initially.
|
|
307
|
+
// BUT we also want to fetch the latest component code in background.
|
|
308
|
+
// DynamicComponent uses 'componentCode' prop if present.
|
|
309
|
+
// We need to fetch the code separately and override the prop passed to DynamicComponent.
|
|
268
310
|
|
|
269
|
-
|
|
311
|
+
// We need to fetch the latest component JS in optimistic mode
|
|
312
|
+
// The DynamicComponent currently logic: if (componentCode) loadAMDModule(code) else fetchDep(name).
|
|
313
|
+
// We want: render with initial componentCode, BUT ALSO fetch new code, and when valid, switch to it.
|
|
314
|
+
|
|
315
|
+
const [optimisticCode, setOptimisticCode] = useState<string | undefined>(
|
|
316
|
+
componentCode
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
// If in optimistic mode, we want to fetch the latest code
|
|
321
|
+
if (isOptimisticMode && appId && pageId && !isLocalMode()) {
|
|
322
|
+
// We can reuse the logic from DynamicComponent/dynamicModules roughly, or just fetch text.
|
|
323
|
+
// The URL logic is in dynamicModules.
|
|
324
|
+
// Let's just rely on DynamicComponent to do the fetching if we pass undefined?
|
|
325
|
+
// No, if we pass undefined it will suspend/show fallback. We want to show OLD code.
|
|
326
|
+
|
|
327
|
+
// So we renders with `optimisticCode` (initially bundled).
|
|
328
|
+
// We fire a background fetch. on success, updated `optimisticCode`.
|
|
329
|
+
|
|
330
|
+
const fetchLatestCode = async () => {
|
|
331
|
+
try {
|
|
332
|
+
// Replicating URL logic from dynamicModules for remote mode
|
|
333
|
+
const { CONST_COMPONENTS_CDN_URL } = await import("../../constants");
|
|
334
|
+
const cacheBuster = Math.round(Date.now() / 1000);
|
|
335
|
+
const name = `${appId}/draft/components/${pageId}`;
|
|
336
|
+
const url = `${CONST_COMPONENTS_CDN_URL}/${name}.js?cacheBuster=${cacheBuster}`;
|
|
337
|
+
|
|
338
|
+
const resp = await fetch(url);
|
|
339
|
+
if (resp.ok) {
|
|
340
|
+
const text = await resp.text();
|
|
341
|
+
// Only update if different?
|
|
342
|
+
// Comparing huge strings might be expensive, but React state update checks equality anyway (ref).
|
|
343
|
+
// Let's just set it.
|
|
344
|
+
setOptimisticCode(text);
|
|
345
|
+
logger.debug("Refreshed optimistic component code");
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
logger.warn("Failed to background refresh optimistic component", e);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
fetchLatestCode();
|
|
353
|
+
} else {
|
|
354
|
+
// If prop changes (e.g. from HMR or parent), sync it
|
|
355
|
+
setOptimisticCode(componentCode);
|
|
356
|
+
}
|
|
357
|
+
}, [componentCode, isOptimisticMode, appId, pageId]);
|
|
270
358
|
|
|
271
|
-
|
|
272
|
-
const
|
|
359
|
+
// Use the calculated optimistic code or the passed prop depending on mode
|
|
360
|
+
const effectiveComponentCode = isOptimisticMode
|
|
361
|
+
? optimisticCode
|
|
362
|
+
: componentCode;
|
|
273
363
|
|
|
274
|
-
logger.debug(
|
|
364
|
+
logger.debug("Page data loaded", {
|
|
275
365
|
hasData: !!pageData?.data,
|
|
276
|
-
dataType: typeof pageData?.data
|
|
366
|
+
dataType: typeof pageData?.data,
|
|
277
367
|
});
|
|
278
368
|
|
|
279
369
|
// Memoize the context object to prevent infinite re-renders
|
|
280
370
|
// Only recreate when the actual data changes, not on every render
|
|
281
371
|
const context = useMemo(() => {
|
|
282
372
|
let clientData = pageData.data?.clientData;
|
|
283
|
-
logger.debug(
|
|
373
|
+
logger.debug("Building context", {
|
|
284
374
|
hasClientData: !!clientData,
|
|
285
|
-
clientDataKeys: Object.keys(clientData || {}).length
|
|
375
|
+
clientDataKeys: Object.keys(clientData || {}).length,
|
|
286
376
|
});
|
|
287
377
|
|
|
288
378
|
// Apply layout heuristics to fix common issues
|
|
@@ -407,7 +497,7 @@ const EncoreApp = ({
|
|
|
407
497
|
name={`${appId}/draft/components/${pageId}`}
|
|
408
498
|
fallback={fallback}
|
|
409
499
|
reloadKey={reloadKey}
|
|
410
|
-
componentCode={
|
|
500
|
+
componentCode={effectiveComponentCode}
|
|
411
501
|
/>
|
|
412
502
|
</EncoreContextProviders>
|
|
413
503
|
</Suspense>
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = "0.1.
|
|
1
|
+
export const PACKAGE_VERSION = "0.1.35";
|