@xeonr/renderer-sdk 1.0.4 → 1.1.0

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Drop-in replacement for `react-dom/client` that auto-wraps the rendered
3
+ * tree in `<RendererErrorBoundary>`. Used by `@xeonr/renderer-plugin-vite`
4
+ * to rewrite renderer-archive build imports so the boundary is mandatory
5
+ * without renderer authors having to opt in.
6
+ *
7
+ * Renderer authors do NOT need to import this directly — the vite plugin
8
+ * substitutes `react-dom/client` specifiers in renderer source code at
9
+ * build time. If a renderer is built outside the plugin (e.g. a custom
10
+ * pipeline), authors can import from here explicitly instead.
11
+ *
12
+ * Mirrors React 18+ `Root` / `RootOptions` surface; passes everything
13
+ * through unchanged except for `render` and `hydrateRoot`'s initial
14
+ * children, which are wrapped in the boundary so a single render-time
15
+ * crash is forwarded to the host overlay rather than tearing down the
16
+ * iframe silently.
17
+ */
18
+ import {
19
+ createRoot as origCreateRoot,
20
+ hydrateRoot as origHydrateRoot,
21
+ type Root,
22
+ type RootOptions,
23
+ type HydrationOptions,
24
+ } from 'react-dom/client';
25
+ import { createElement, type ReactNode } from 'react';
26
+ import { RendererErrorBoundary } from './RendererErrorBoundary.js';
27
+
28
+ function wrap(children: ReactNode): ReactNode {
29
+ return createElement(RendererErrorBoundary, null, children);
30
+ }
31
+
32
+ export function createRoot(container: Element | DocumentFragment, options?: RootOptions): Root {
33
+ const root = origCreateRoot(container, options);
34
+ const originalRender = root.render.bind(root);
35
+ root.render = (children: ReactNode) => originalRender(wrap(children));
36
+ return root;
37
+ }
38
+
39
+ export function hydrateRoot(
40
+ container: Element | Document,
41
+ initialChildren: ReactNode,
42
+ options?: HydrationOptions,
43
+ ): Root {
44
+ const root = origHydrateRoot(container, wrap(initialChildren), options);
45
+ const originalRender = root.render.bind(root);
46
+ root.render = (children: ReactNode) => originalRender(wrap(children));
47
+ return root;
48
+ }
49
+
50
+ // Re-export every remaining symbol from `react-dom/client` so renderers
51
+ // importing other named bindings (e.g. `Root` type) continue to work
52
+ // after the specifier rewrite.
53
+ export type { Root, RootOptions, HydrationOptions } from 'react-dom/client';
@@ -1,2 +1,4 @@
1
1
  export { useRendererClient } from './useRendererClient.js';
2
2
  export type { UseRendererClientOptions, UseRendererClientResult } from './useRendererClient.js';
3
+ export { RendererErrorBoundary } from './RendererErrorBoundary.js';
4
+ export type { RendererErrorBoundaryProps } from './RendererErrorBoundary.js';
@@ -5,6 +5,25 @@ import type { RendererApiAdapter, RendererConfig, RendererScope, RenderingType,
5
5
 
6
6
  export interface UseRendererClientOptions extends RendererClientOptions {}
7
7
 
8
+ /**
9
+ * Apply the host's theme to the renderer document. Two distinct effects:
10
+ *
11
+ * - `documentElement.dataset.theme` — surfaces the theme as a
12
+ * `[data-theme="dark"]` selector for renderer-authored CSS.
13
+ * - `documentElement.style.colorScheme` — tells the browser to render
14
+ * scrollbars, form controls, and (critically) the iframe's default
15
+ * canvas in the matching mode. Without this, a cross-origin iframe
16
+ * defaults to a white canvas regardless of what the host's
17
+ * `color-scheme` is — so `background: transparent` in the renderer
18
+ * reveals white, not the host's dark surface. This is the fix for
19
+ * "renderer shows light mode when host is dark".
20
+ */
21
+ function applyThemeToDocument(theme: 'light' | 'dark'): void {
22
+ if (typeof document === 'undefined') return;
23
+ document.documentElement.dataset.theme = theme;
24
+ document.documentElement.style.colorScheme = theme;
25
+ }
26
+
8
27
  export interface UseRendererClientResult {
9
28
  /** Whether the host has sent the init message. */
10
29
  connected: boolean;
@@ -95,12 +114,12 @@ export function useRendererClient(options?: UseRendererClientOptions): UseRender
95
114
  setEntrypoint(payload.entrypoint);
96
115
  setApiBaseUrl(payload.apiBaseUrl);
97
116
  setPath(client.getPath());
98
- document.documentElement.dataset.theme = payload.theme;
117
+ applyThemeToDocument(payload.theme);
99
118
  });
100
119
 
101
120
  const unsubTheme = client.onThemeChange((newTheme) => {
102
121
  setTheme(newTheme);
103
- document.documentElement.dataset.theme = newTheme;
122
+ applyThemeToDocument(newTheme);
104
123
  });
105
124
 
106
125
  const unsubToken = client.onTokenRefresh((newToken, newExpiresAt) => {
package/src/types.ts CHANGED
@@ -11,6 +11,27 @@ export interface RendererConfig {
11
11
  allowForms?: boolean;
12
12
  allowDownloads?: boolean;
13
13
  };
14
+ /**
15
+ * Extra origins this renderer needs to reach via `fetch` / `XMLHttpRequest`
16
+ * / WebSocket. Surfaced to the browser as additional entries in the CSP
17
+ * `connect-src` directive. The upl.im API + auth hosts are always allowed
18
+ * by the renderer-proxy; only list extras here (e.g. a third-party API
19
+ * the renderer integrates with).
20
+ *
21
+ * Each entry is a fully-qualified origin like `https://api.example.com`
22
+ * or a wildcarded host like `https://*.example.com`. No paths, no
23
+ * trailing slashes. Origins are validated at deploy time.
24
+ */
25
+ connectHosts?: string[];
26
+ /**
27
+ * Short build identifier the build pipeline stamps into config.json
28
+ * (e.g. the renderer-plugin-vite hash of the output directory).
29
+ * Surfaced in crash telemetry as a separate dimension so we can
30
+ * distinguish "crashed in build A" from "crashed in build B" even
31
+ * when both builds share the same archive_upload_id (the developer
32
+ * republished without bumping the archive). Bounded to 64 chars.
33
+ */
34
+ buildHash?: string;
14
35
  }
15
36
 
16
37
  export type RendererPermission =