@bravostudioai/react 0.1.31 → 0.1.34

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.
Files changed (83) hide show
  1. package/dist/cli/commands/download.js +22 -39
  2. package/dist/cli/commands/download.js.map +1 -1
  3. package/dist/cli/commands/generate.js +130 -147
  4. package/dist/cli/commands/generate.js.map +1 -1
  5. package/dist/cli.js +21 -24
  6. package/dist/cli.js.map +1 -1
  7. package/dist/codegen/generator.js +126 -125
  8. package/dist/codegen/generator.js.map +1 -1
  9. package/dist/codegen/parser.js +256 -593
  10. package/dist/codegen/parser.js.map +1 -1
  11. package/dist/codegen/propQualification.js +117 -0
  12. package/dist/codegen/propQualification.js.map +1 -0
  13. package/dist/components/DynamicComponent.js.map +1 -1
  14. package/dist/components/EncoreApp.js +156 -265
  15. package/dist/components/EncoreApp.js.map +1 -1
  16. package/dist/components/EncoreContextProviders.js +24 -0
  17. package/dist/components/EncoreContextProviders.js.map +1 -0
  18. package/dist/components.js +179 -174
  19. package/dist/components.js.map +1 -1
  20. package/dist/hooks/useFontLoader.js +41 -0
  21. package/dist/hooks/useFontLoader.js.map +1 -0
  22. package/dist/hooks/usePusherUpdates.js +41 -45
  23. package/dist/hooks/usePusherUpdates.js.map +1 -1
  24. package/dist/hooks/useRepeatingContainers.js +79 -0
  25. package/dist/hooks/useRepeatingContainers.js.map +1 -0
  26. package/dist/index.js +13 -14
  27. package/dist/index.js.map +1 -1
  28. package/dist/lib/dataPatching.js +24 -0
  29. package/dist/lib/dataPatching.js.map +1 -0
  30. package/dist/lib/dynamicModules.js +44 -45
  31. package/dist/lib/dynamicModules.js.map +1 -1
  32. package/dist/lib/fetcher.js +6 -13
  33. package/dist/lib/fetcher.js.map +1 -1
  34. package/dist/lib/logger.js +35 -0
  35. package/dist/lib/logger.js.map +1 -0
  36. package/dist/lib/moduleRegistry.js +9 -8
  37. package/dist/lib/moduleRegistry.js.map +1 -1
  38. package/dist/src/cli/commands/download.d.ts +1 -1
  39. package/dist/src/cli/commands/download.d.ts.map +1 -1
  40. package/dist/src/cli/commands/generate.d.ts +1 -1
  41. package/dist/src/cli/commands/generate.d.ts.map +1 -1
  42. package/dist/src/codegen/generator.d.ts +75 -1
  43. package/dist/src/codegen/generator.d.ts.map +1 -1
  44. package/dist/src/codegen/parser.d.ts +39 -0
  45. package/dist/src/codegen/parser.d.ts.map +1 -1
  46. package/dist/src/codegen/propQualification.d.ts +42 -0
  47. package/dist/src/codegen/propQualification.d.ts.map +1 -0
  48. package/dist/src/components/DynamicComponent.d.ts +1 -1
  49. package/dist/src/components/DynamicComponent.d.ts.map +1 -1
  50. package/dist/src/components/EncoreApp.d.ts +58 -3
  51. package/dist/src/components/EncoreApp.d.ts.map +1 -1
  52. package/dist/src/components/EncoreContextProviders.d.ts +34 -0
  53. package/dist/src/components/EncoreContextProviders.d.ts.map +1 -0
  54. package/dist/src/components.d.ts.map +1 -1
  55. package/dist/src/hooks/useFontLoader.d.ts +17 -0
  56. package/dist/src/hooks/useFontLoader.d.ts.map +1 -0
  57. package/dist/src/hooks/usePusherUpdates.d.ts.map +1 -1
  58. package/dist/src/hooks/useRepeatingContainers.d.ts +31 -0
  59. package/dist/src/hooks/useRepeatingContainers.d.ts.map +1 -0
  60. package/dist/src/index.d.ts +2 -0
  61. package/dist/src/index.d.ts.map +1 -1
  62. package/dist/src/lib/dataPatching.d.ts +18 -0
  63. package/dist/src/lib/dataPatching.d.ts.map +1 -0
  64. package/dist/src/lib/dynamicModules.d.ts.map +1 -1
  65. package/dist/src/lib/fetcher.d.ts.map +1 -1
  66. package/dist/src/lib/logger.d.ts +33 -0
  67. package/dist/src/lib/logger.d.ts.map +1 -0
  68. package/dist/src/lib/moduleRegistry.d.ts.map +1 -1
  69. package/dist/src/stores/useEncoreState.d.ts +43 -1
  70. package/dist/src/stores/useEncoreState.d.ts.map +1 -1
  71. package/dist/src/version.d.ts +1 -1
  72. package/dist/stores/useEncoreState.js.map +1 -1
  73. package/dist/version.js +1 -1
  74. package/dist/version.js.map +1 -1
  75. package/package.json +2 -1
  76. package/src/cli/commands/download.ts +8 -28
  77. package/src/cli/commands/generate.ts +44 -45
  78. package/src/cli/index.ts +49 -32
  79. package/src/codegen/generator.ts +6 -3
  80. package/src/components/DynamicComponent.tsx +1 -1
  81. package/src/components/EncoreApp.tsx +111 -21
  82. package/src/hooks/useRepeatingContainers.ts +1 -1
  83. 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('EncoreApp render', { appId, pageId });
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
- // If appDefinition is provided, disable SWR fetch by setting url to null
214
- const appUrl = appDefinition
215
- ? null
216
- : appId && `/devices/apps/${appId}${useLocalFlag ? "?useLocal=1" : ""}`;
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, // Only use suspense if we are fetching
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 = appDefinition ? { data: appDefinition } : appSWR;
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 pageUrl = pageDefinition
262
- ? null
263
- : appId &&
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
- logger.debug('Page data fetch', { pageUrl });
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
- const pageSWR = useSWR(pageUrl, fetcher, { suspense: !!pageUrl });
272
- const pageData = pageDefinition ? { data: pageDefinition } : pageSWR;
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('Page data loaded', {
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('Building context', {
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={componentCode}
500
+ componentCode={effectiveComponentCode}
411
501
  />
412
502
  </EncoreContextProviders>
413
503
  </Suspense>
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { useState, useCallback, useMemo, useEffect } from "react";
9
- import React from "react";
9
+
10
10
  import type { RepeatingContainerControl } from "../contexts/EncoreRepeatingContainerContext";
11
11
 
12
12
  interface ContainerControlProps {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "0.1.31";
1
+ export const PACKAGE_VERSION = "0.1.34";