@xeonr/renderer-sdk 1.0.5 → 1.3.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.
Files changed (44) hide show
  1. package/dist/client.d.ts +62 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +150 -2
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/protocol.d.ts +65 -1
  10. package/dist/protocol.d.ts.map +1 -1
  11. package/dist/protocol.js +91 -0
  12. package/dist/protocol.js.map +1 -1
  13. package/dist/react/RendererErrorBoundary.d.ts +60 -0
  14. package/dist/react/RendererErrorBoundary.d.ts.map +1 -0
  15. package/dist/react/RendererErrorBoundary.js +37 -0
  16. package/dist/react/RendererErrorBoundary.js.map +1 -0
  17. package/dist/react/dom.d.ts +23 -0
  18. package/dist/react/dom.d.ts.map +1 -0
  19. package/dist/react/dom.js +36 -0
  20. package/dist/react/dom.js.map +1 -0
  21. package/dist/react/index.d.ts +3 -0
  22. package/dist/react/index.d.ts.map +1 -1
  23. package/dist/react/index.js +2 -0
  24. package/dist/react/index.js.map +1 -1
  25. package/dist/react/useRendererClient.d.ts +9 -0
  26. package/dist/react/useRendererClient.d.ts.map +1 -1
  27. package/dist/react/useRendererClient.js +80 -18
  28. package/dist/react/useRendererClient.js.map +1 -1
  29. package/dist/react/useReportFatalError.d.ts +37 -0
  30. package/dist/react/useReportFatalError.d.ts.map +1 -0
  31. package/dist/react/useReportFatalError.js +71 -0
  32. package/dist/react/useReportFatalError.js.map +1 -0
  33. package/dist/types.d.ts +40 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/package.json +12 -2
  36. package/src/client.ts +166 -2
  37. package/src/index.ts +4 -0
  38. package/src/protocol.ts +143 -1
  39. package/src/react/RendererErrorBoundary.tsx +95 -0
  40. package/src/react/dom.ts +53 -0
  41. package/src/react/index.ts +3 -0
  42. package/src/react/useRendererClient.ts +95 -21
  43. package/src/react/useReportFatalError.tsx +71 -0
  44. package/src/types.ts +40 -0
@@ -0,0 +1,60 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ /**
3
+ * React error boundary that catches render-time exceptions and forwards
4
+ * them to the host via `uplim:crash`. **Renderers should wrap their root
5
+ * in this component** — the SDK's global window hooks catch async
6
+ * failures, but they cannot catch errors thrown during React render or
7
+ * commit (those abort the tree before bubbling to `window.error`).
8
+ *
9
+ * Deliberately renders **nothing** on crash. The host owns the overlay
10
+ * UI for two reasons:
11
+ * 1. Styling stays consistent with the dashboard chrome — a renderer's
12
+ * CSS may itself be the thing that broke, so painting an inline
13
+ * fallback inside the iframe risks an invisible / unreadable
14
+ * screen.
15
+ * 2. The host's overlay can offer recovery actions the renderer
16
+ * can't, like "open the built-in preview instead" (which involves
17
+ * tearing this iframe down and rendering something else).
18
+ *
19
+ * Renderers may pass a `fallback` if they want a minimal visible
20
+ * placeholder while the host's overlay paints (e.g. so users don't
21
+ * stare at a transparent iframe for a frame), but the host overlay
22
+ * remains the source of truth.
23
+ *
24
+ * Usage:
25
+ *
26
+ * ```tsx
27
+ * import { createRoot } from 'react-dom/client';
28
+ * import { RendererErrorBoundary } from '@xeonr/renderer-sdk/react';
29
+ *
30
+ * createRoot(document.getElementById('root')!).render(
31
+ * <RendererErrorBoundary>
32
+ * <App />
33
+ * </RendererErrorBoundary>
34
+ * );
35
+ * ```
36
+ */
37
+ export interface RendererErrorBoundaryProps {
38
+ children: ReactNode;
39
+ /** Optional inline fallback rendered after the crash is reported. Default: nothing. */
40
+ fallback?: ReactNode;
41
+ /**
42
+ * Optional targetOrigin override for the postMessage. Matches
43
+ * `RendererClientOptions.targetOrigin`. Defaults to `'*'` because at
44
+ * crash time we don't want to gate telemetry on origin matching.
45
+ */
46
+ targetOrigin?: string;
47
+ /** Optional hook called after the host has been notified — useful for sentry / console wiring. */
48
+ onCrash?: (error: unknown, info: ErrorInfo) => void;
49
+ }
50
+ interface State {
51
+ crashed: boolean;
52
+ }
53
+ export declare class RendererErrorBoundary extends Component<RendererErrorBoundaryProps, State> {
54
+ state: State;
55
+ static getDerivedStateFromError(): State;
56
+ componentDidCatch(error: unknown, info: ErrorInfo): void;
57
+ render(): ReactNode;
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=RendererErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RendererErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/react/RendererErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,SAAS,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CACpD;AAED,UAAU,KAAK;IACd,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,qBAAsB,SAAQ,SAAS,CAAC,0BAA0B,EAAE,KAAK,CAAC;IACtF,KAAK,EAAE,KAAK,CAAsB;IAElC,MAAM,CAAC,wBAAwB,IAAI,KAAK;IAIxC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAyBxD,MAAM,IAAI,SAAS;CAMnB"}
@@ -0,0 +1,37 @@
1
+ import { Component } from 'react';
2
+ import { buildCrashMessage, postCrashToHost } from '../protocol.js';
3
+ export class RendererErrorBoundary extends Component {
4
+ state = { crashed: false };
5
+ static getDerivedStateFromError() {
6
+ return { crashed: true };
7
+ }
8
+ componentDidCatch(error, info) {
9
+ // Build the crash payload via the shared helper so global hooks and the
10
+ // boundary produce identical wire shapes.
11
+ const msg = buildCrashMessage('render', error);
12
+ // React stack is more useful than the JS stack for render-time bugs —
13
+ // it points at the component path, not just the throwing function. We
14
+ // append it so server-side logs preserve both.
15
+ if (info?.componentStack) {
16
+ const componentStack = `\n\nReact component stack:\n${info.componentStack}`;
17
+ // Respect the proto's stack limit (32768) — buildCrashMessage already
18
+ // clamped, but we have new content to add, so re-clamp the combined value.
19
+ const combined = (msg.stack ? `${msg.stack}\n` : '') + componentStack;
20
+ msg.stack = combined.length > 32768 ? combined.slice(0, 32768 - 12) + '…[truncated]' : combined;
21
+ }
22
+ postCrashToHost(msg, this.props.targetOrigin ?? '*');
23
+ try {
24
+ this.props.onCrash?.(error, info);
25
+ }
26
+ catch {
27
+ // Best-effort: never let the consumer's handler resurface the crash.
28
+ }
29
+ }
30
+ render() {
31
+ if (this.state.crashed) {
32
+ return this.props.fallback ?? null;
33
+ }
34
+ return this.props.children;
35
+ }
36
+ }
37
+ //# sourceMappingURL=RendererErrorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RendererErrorBoundary.js","sourceRoot":"","sources":["../../src/react/RendererErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAuDpE,MAAM,OAAO,qBAAsB,SAAQ,SAA4C;IACtF,KAAK,GAAU,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAElC,MAAM,CAAC,wBAAwB;QAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,IAAe;QAChD,wEAAwE;QACxE,0CAA0C;QAC1C,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAE/C,sEAAsE;QACtE,sEAAsE;QACtE,+CAA+C;QAC/C,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YAC1B,MAAM,cAAc,GAAG,+BAA+B,IAAI,CAAC,cAAc,EAAE,CAAC;YAC5E,sEAAsE;YACtE,2EAA2E;YAC3E,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC;YACtE,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjG,CAAC;QAED,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC;QAErD,IAAI,CAAC;YACJ,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACR,qEAAqE;QACtE,CAAC;IACF,CAAC;IAED,MAAM;QACL,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC5B,CAAC;CACD"}
@@ -0,0 +1,23 @@
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 { type Root, type RootOptions, type HydrationOptions } from 'react-dom/client';
19
+ import { type ReactNode } from 'react';
20
+ export declare function createRoot(container: Element | DocumentFragment, options?: RootOptions): Root;
21
+ export declare function hydrateRoot(container: Element | Document, initialChildren: ReactNode, options?: HydrationOptions): Root;
22
+ export type { Root, RootOptions, HydrationOptions } from 'react-dom/client';
23
+ //# sourceMappingURL=dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/react/dom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGN,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOtD,wBAAgB,UAAU,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAK7F;AAED,wBAAgB,WAAW,CAC1B,SAAS,EAAE,OAAO,GAAG,QAAQ,EAC7B,eAAe,EAAE,SAAS,EAC1B,OAAO,CAAC,EAAE,gBAAgB,GACxB,IAAI,CAKN;AAKD,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,36 @@
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 { createRoot as origCreateRoot, hydrateRoot as origHydrateRoot, } from 'react-dom/client';
19
+ import { createElement } from 'react';
20
+ import { RendererErrorBoundary } from './RendererErrorBoundary.js';
21
+ function wrap(children) {
22
+ return createElement(RendererErrorBoundary, null, children);
23
+ }
24
+ export function createRoot(container, options) {
25
+ const root = origCreateRoot(container, options);
26
+ const originalRender = root.render.bind(root);
27
+ root.render = (children) => originalRender(wrap(children));
28
+ return root;
29
+ }
30
+ export function hydrateRoot(container, initialChildren, options) {
31
+ const root = origHydrateRoot(container, wrap(initialChildren), options);
32
+ const originalRender = root.render.bind(root);
33
+ root.render = (children) => originalRender(wrap(children));
34
+ return root;
35
+ }
36
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/react/dom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EACN,UAAU,IAAI,cAAc,EAC5B,WAAW,IAAI,eAAe,GAI9B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,SAAS,IAAI,CAAC,QAAmB;IAChC,OAAO,aAAa,CAAC,qBAAqB,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAqC,EAAE,OAAqB;IACtF,MAAM,IAAI,GAAG,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,GAAG,CAAC,QAAmB,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAC1B,SAA6B,EAC7B,eAA0B,EAC1B,OAA0B;IAE1B,MAAM,IAAI,GAAG,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,GAAG,CAAC,QAAmB,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -1,3 +1,6 @@
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
+ export { useReportFatalError } from './useReportFatalError.js';
3
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1,2 +1,4 @@
1
1
  export { useRendererClient } from './useRendererClient.js';
2
+ export { RendererErrorBoundary } from './RendererErrorBoundary.js';
3
+ export { useReportFatalError } from './useReportFatalError.js';
2
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -32,6 +32,15 @@ export interface UseRendererClientResult {
32
32
  requestToken: () => void;
33
33
  /** Request the host to close this renderer (modal mode). */
34
34
  close: () => void;
35
+ /**
36
+ * When the renderer's `config.json` has `deferReady: true`, call
37
+ * this once the renderer's data is loaded and the UI is ready to
38
+ * be visible. The host overlay stays up until this fires (or until
39
+ * the SDK's safety timeout elapses — see
40
+ * `RendererClientOptions.readyTimeoutMs`). No-op when `deferReady`
41
+ * isn't set in config.
42
+ */
43
+ markReady: () => void;
35
44
  /** The underlying RendererClient instance for advanced use. */
36
45
  client: RendererClient;
37
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useRendererClient.d.ts","sourceRoot":"","sources":["../../src/react/useRendererClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAEjH,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;CAAG;AAqB1E,MAAM,WAAW,uBAAuB;IACvC,kDAAkD;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,2CAA2C;IAC3C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kDAAkD;IAClD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8BAA8B;IAC9B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,wDAAwD;IACxD,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,yDAAyD;IACzD,UAAU,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC1C,qCAAqC;IACrC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,0EAA0E;IAC1E,UAAU,EAAE,kBAAkB,CAAC;IAC/B,oGAAoG;IACpG,IAAI,EAAE,MAAM,CAAC;IAEb,kDAAkD;IAClD,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,+DAA+D;IAC/D,MAAM,EAAE,cAAc,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,uBAAuB,CAgF7F"}
1
+ {"version":3,"file":"useRendererClient.d.ts","sourceRoot":"","sources":["../../src/react/useRendererClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAEjH,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;CAAG;AAiF1E,MAAM,WAAW,uBAAuB;IACvC,kDAAkD;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,2CAA2C;IAC3C,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kDAAkD;IAClD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8BAA8B;IAC9B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,wDAAwD;IACxD,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,yDAAyD;IACzD,UAAU,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC1C,qCAAqC;IACrC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,0EAA0E;IAC1E,UAAU,EAAE,kBAAkB,CAAC;IAC/B,oGAAoG;IACpG,IAAI,EAAE,MAAM,CAAC;IAEb,kDAAkD;IAClD,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;IAEtB,+DAA+D;IAC/D,MAAM,EAAE,cAAc,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,uBAAuB,CAqF7F"}
@@ -1,5 +1,60 @@
1
- import { useState, useRef, useEffect, useMemo } from 'react';
1
+ import { useState, useEffect, useMemo } from 'react';
2
2
  import { RendererClient } from '../client.js';
3
+ // One RendererClient per page. Multiple `useRendererClient()` calls
4
+ // (different components subscribing to the same state) all share the
5
+ // singleton — previously each call spawned its own client, which
6
+ // caused duplicate `window.error` / `unhandledrejection` listeners
7
+ // (so every crash got reported N times), duplicate `uplim:ready`
8
+ // pings, and state divergence between instances.
9
+ //
10
+ // Page lifetime: the iframe is full-reloaded for HMR / test, so the
11
+ // module-level singleton lives exactly as long as it should. SDK-level
12
+ // HMR (developer working on the SDK in monorepo, hot-replacing this
13
+ // module) is handled via `import.meta.hot.dispose` at the bottom of
14
+ // the file.
15
+ let sharedClient = null;
16
+ let sharedClientOptionsKey = null;
17
+ function optionsKey(options) {
18
+ if (!options)
19
+ return '';
20
+ // Stable order so { a, b } and { b, a } produce the same key. Used
21
+ // only for the dev-mode mismatch warning — never for behavior.
22
+ const entries = Object.entries(options).sort(([a], [b]) => a.localeCompare(b));
23
+ return JSON.stringify(entries);
24
+ }
25
+ function getSharedClient(options) {
26
+ if (sharedClient) {
27
+ const key = optionsKey(options);
28
+ // Best-effort dev nudge: if a later mount passes different
29
+ // options than the first one, those options are silently
30
+ // ignored. Only flag when keys differ AND the later caller
31
+ // passed non-empty options (passing nothing is the common
32
+ // case for downstream subscribers).
33
+ if (key && sharedClientOptionsKey !== null && key !== sharedClientOptionsKey) {
34
+ // eslint-disable-next-line no-console
35
+ console.warn('[uplim] useRendererClient called with different options after first call; ' +
36
+ 'later options are ignored (the client is a per-page singleton).');
37
+ }
38
+ return sharedClient;
39
+ }
40
+ sharedClient = new RendererClient(options);
41
+ sharedClientOptionsKey = optionsKey(options);
42
+ return sharedClient;
43
+ }
44
+ // SDK-dev HMR cleanup: when this module is hot-replaced (e.g. someone
45
+ // editing the SDK with the renderer harness running), detach the old
46
+ // client's listeners before the new module evaluates. Prod / non-HMR
47
+ // builds skip the entire block — `import.meta.hot` is undefined.
48
+ const maybeHot = import.meta.hot;
49
+ if (maybeHot) {
50
+ maybeHot.dispose(() => {
51
+ if (sharedClient) {
52
+ sharedClient.destroy();
53
+ sharedClient = null;
54
+ sharedClientOptionsKey = null;
55
+ }
56
+ });
57
+ }
3
58
  /**
4
59
  * Apply the host's theme to the renderer document. Two distinct effects:
5
60
  *
@@ -42,22 +97,22 @@ function applyThemeToDocument(theme) {
42
97
  * ```
43
98
  */
44
99
  export function useRendererClient(options) {
45
- const clientRef = useRef(null);
46
- // Lazy-init the client (only once)
47
- if (clientRef.current === null) {
48
- clientRef.current = new RendererClient(options);
49
- }
50
- const client = clientRef.current;
51
- const [connected, setConnected] = useState(false);
52
- const [scope, setScope] = useState(null);
53
- const [renderingType, setRenderingType] = useState(null);
54
- const [token, setToken] = useState(null);
55
- const [tokenExpiresAt, setTokenExpiresAt] = useState(null);
56
- const [theme, setTheme] = useState('light');
57
- const [config, setConfig] = useState(null);
58
- const [entrypoint, setEntrypoint] = useState(null);
59
- const [apiBaseUrl, setApiBaseUrl] = useState(null);
60
- const [path, setPath] = useState('/');
100
+ const client = getSharedClient(options);
101
+ // Initial state read directly from the client handles late
102
+ // subscribers (a second `useRendererClient` mount that happens
103
+ // after init has already fired). Without this, the late mount
104
+ // would sit at `connected: false` forever because onInit only
105
+ // fires for NEW init events, not for already-received state.
106
+ const [connected, setConnected] = useState(() => client.isConnected());
107
+ const [scope, setScope] = useState(() => client.getScope());
108
+ const [renderingType, setRenderingType] = useState(() => client.getRenderingType());
109
+ const [token, setToken] = useState(() => client.getToken());
110
+ const [tokenExpiresAt, setTokenExpiresAt] = useState(() => client.getTokenExpiresAt());
111
+ const [theme, setTheme] = useState(() => client.getTheme());
112
+ const [config, setConfig] = useState(() => client.getConfig());
113
+ const [entrypoint, setEntrypoint] = useState(() => client.getEntrypoint());
114
+ const [apiBaseUrl, setApiBaseUrl] = useState(() => client.getApiBaseUrl());
115
+ const [path, setPath] = useState(() => client.getPath());
61
116
  useEffect(() => {
62
117
  const unsubInit = client.onInit((payload) => {
63
118
  setConnected(true);
@@ -88,13 +143,20 @@ export function useRendererClient(options) {
88
143
  unsubTheme();
89
144
  unsubToken();
90
145
  unsubNavigate();
91
- client.destroy();
146
+ // NB: do NOT destroy() here — the client is a per-page
147
+ // singleton, not a per-component instance. Component
148
+ // unmounts (route changes / strict-mode double-mount /
149
+ // React render-time tearing) shouldn't kill the shared
150
+ // state. The iframe full-reload tears the singleton down
151
+ // implicitly when the page goes away; SDK-level HMR is
152
+ // handled by import.meta.hot.dispose at the top of file.
92
153
  };
93
154
  }, [client]);
94
155
  const methods = useMemo(() => ({
95
156
  openUpload: (uploadId) => client.openUpload(uploadId),
96
157
  requestToken: () => client.requestToken(),
97
158
  close: () => client.close(),
159
+ markReady: () => client.signalReady(),
98
160
  apiAdapter: client.getApiAdapter(),
99
161
  }), [client]);
100
162
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useRendererClient.js","sourceRoot":"","sources":["../../src/react/useRendererClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useRendererClient.js","sourceRoot":"","sources":["../../src/react/useRendererClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Hook for surfacing async failures to `<RendererErrorBoundary>` (and from
3
+ * there to the host's crash overlay + telemetry).
4
+ *
5
+ * Renderers shouldn't render their own "Upload not found" / "Failed to
6
+ * load" inline UI — those are still crashes from a telemetry standpoint,
7
+ * and the host already owns a polished overlay with reload + fallback
8
+ * actions. This hook lets a renderer take an error caught in an async
9
+ * context (fetch `.catch()`, useEffect cleanup, etc.) and route it to
10
+ * the host instead of rendering a half-broken inline state.
11
+ *
12
+ * Pattern:
13
+ *
14
+ * ```tsx
15
+ * const reportFatal = useReportFatalError();
16
+ * useEffect(() => {
17
+ * client.getUpload(...).catch(reportFatal);
18
+ * }, []);
19
+ * ```
20
+ *
21
+ * Mechanics: we keep an error in component state. When set, the *next*
22
+ * render throws it — React's error-handling pipeline catches the throw
23
+ * and bubbles to the nearest boundary. From there
24
+ * `RendererErrorBoundary.componentDidCatch` reports the crash to the
25
+ * host and renders nothing, and the host overlay (Reload / Copy /
26
+ * Fallback) takes over the visible iframe area.
27
+ *
28
+ * Why state-then-throw rather than a direct `throw err`? You can't
29
+ * throw to React from async code — React only sees throws that happen
30
+ * during render or in event handlers. Routing through state ensures
31
+ * the throw lands inside the render phase.
32
+ *
33
+ * The returned callback is stable across renders so it's safe to drop
34
+ * straight into a `.catch()`.
35
+ */
36
+ export declare function useReportFatalError(): (err: unknown) => void;
37
+ //# sourceMappingURL=useReportFatalError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useReportFatalError.d.ts","sourceRoot":"","sources":["../../src/react/useReportFatalError.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,mBAAmB,IAAI,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAiC5D"}
@@ -0,0 +1,71 @@
1
+ import { useCallback, useState } from 'react';
2
+ /**
3
+ * Hook for surfacing async failures to `<RendererErrorBoundary>` (and from
4
+ * there to the host's crash overlay + telemetry).
5
+ *
6
+ * Renderers shouldn't render their own "Upload not found" / "Failed to
7
+ * load" inline UI — those are still crashes from a telemetry standpoint,
8
+ * and the host already owns a polished overlay with reload + fallback
9
+ * actions. This hook lets a renderer take an error caught in an async
10
+ * context (fetch `.catch()`, useEffect cleanup, etc.) and route it to
11
+ * the host instead of rendering a half-broken inline state.
12
+ *
13
+ * Pattern:
14
+ *
15
+ * ```tsx
16
+ * const reportFatal = useReportFatalError();
17
+ * useEffect(() => {
18
+ * client.getUpload(...).catch(reportFatal);
19
+ * }, []);
20
+ * ```
21
+ *
22
+ * Mechanics: we keep an error in component state. When set, the *next*
23
+ * render throws it — React's error-handling pipeline catches the throw
24
+ * and bubbles to the nearest boundary. From there
25
+ * `RendererErrorBoundary.componentDidCatch` reports the crash to the
26
+ * host and renders nothing, and the host overlay (Reload / Copy /
27
+ * Fallback) takes over the visible iframe area.
28
+ *
29
+ * Why state-then-throw rather than a direct `throw err`? You can't
30
+ * throw to React from async code — React only sees throws that happen
31
+ * during render or in event handlers. Routing through state ensures
32
+ * the throw lands inside the render phase.
33
+ *
34
+ * The returned callback is stable across renders so it's safe to drop
35
+ * straight into a `.catch()`.
36
+ */
37
+ export function useReportFatalError() {
38
+ // Generic state slot — we never read the value, only use the
39
+ // updater. We narrow to `never` so TypeScript stops us from
40
+ // accidentally using it as data anywhere.
41
+ const [, setState] = useState();
42
+ return useCallback((err) => {
43
+ // Functional updater throws — React invokes it during the next
44
+ // render attempt, which puts the throw inside the render phase
45
+ // where boundaries can see it.
46
+ setState(() => {
47
+ if (err instanceof Error) {
48
+ throw err;
49
+ }
50
+ // Wrap non-Error throws so the boundary always sees a real
51
+ // Error instance with a meaningful .name / .message. Without
52
+ // this, `client.reportCrash` would synthesise a generic
53
+ // 'UnknownError' which loses the original payload's shape.
54
+ if (typeof err === 'string') {
55
+ throw new Error(err);
56
+ }
57
+ if (err && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
58
+ const wrapped = new Error(err.message);
59
+ if ('name' in err && typeof err.name === 'string') {
60
+ wrapped.name = err.name;
61
+ }
62
+ if ('stack' in err && typeof err.stack === 'string') {
63
+ wrapped.stack = err.stack;
64
+ }
65
+ throw wrapped;
66
+ }
67
+ throw new Error('Unknown renderer error');
68
+ });
69
+ }, []);
70
+ }
71
+ //# sourceMappingURL=useReportFatalError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useReportFatalError.js","sourceRoot":"","sources":["../../src/react/useReportFatalError.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,mBAAmB;IAClC,6DAA6D;IAC7D,4DAA4D;IAC5D,0CAA0C;IAC1C,MAAM,CAAC,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAS,CAAC;IACvC,OAAO,WAAW,CAAC,CAAC,GAAY,EAAE,EAAE;QACnC,+DAA+D;QAC/D,+DAA+D;QAC/D,+BAA+B;QAC/B,QAAQ,CAAC,GAAG,EAAE;YACb,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;gBAC1B,MAAM,GAAG,CAAC;YACX,CAAC;YACD,2DAA2D;YAC3D,6DAA6D;YAC7D,wDAAwD;YACxD,2DAA2D;YAC3D,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC;YACD,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC3F,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACvC,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACnD,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;gBACzB,CAAC;gBACD,IAAI,OAAO,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACrD,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;gBAC3B,CAAC;gBACD,MAAM,OAAO,CAAC;YACf,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;AACR,CAAC"}
package/dist/types.d.ts CHANGED
@@ -11,6 +11,46 @@ 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;
35
+ /**
36
+ * When true, the SDK holds `uplim:ack` until the renderer explicitly
37
+ * calls `markReady()` from `useRendererClient()`. The host's loading
38
+ * overlay stays visible until that signal arrives, so the user
39
+ * never sees a blank canvas between bridge ack and the renderer's
40
+ * first paint.
41
+ *
42
+ * Leave unset (or `false`) for fast renderers where the gap is
43
+ * invisible — that's the right default and lowest-friction pattern
44
+ * (image, code, plain markdown). Set `true` when the renderer takes
45
+ * perceptible time to settle (large file fetch, video decode,
46
+ * multi-page paginate) and the blank-canvas flash is noticeable.
47
+ *
48
+ * The SDK auto-acks after 30s (configurable via
49
+ * `RendererClientOptions.readyTimeoutMs`) and logs a warning if
50
+ * `markReady()` never fires — so a forgotten signal can't trap
51
+ * users behind the overlay.
52
+ */
53
+ deferReady?: boolean;
14
54
  }
15
55
  export type RendererPermission = 'createFolder' | 'openUpload';
16
56
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,uDAAuD;IACvD,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,cAAc,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;CACF;AAED,MAAM,MAAM,kBAAkB,GAC3B,cAAc,GACd,YAAY,CAAC;AAEhB;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,wBAAwB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,CAAC;AAEnB;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,uBAAuB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;CAC5C;AAED,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC3B,wBAAwB;IACxB,OAAO,EAAE,CAAC,CAAC;IACX,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,mCAAmC;IACnC,UAAU,EAAE,WAAW,GAAG,QAAQ,CAAC;IACnC,+CAA+C;IAC/C,KAAK,EAAE,aAAa,CAAC;IACrB,4DAA4D;IAC5D,aAAa,EAAE,aAAa,CAAC;IAC7B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,MAAM,EAAE,cAAc,CAAC;IACvB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,uDAAuD;IACvD,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,cAAc,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;IACF;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAC3B,cAAc,GACd,YAAY,CAAC;AAEhB;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,wBAAwB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,CAAC;AAEnB;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,uBAAuB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;CAC5C;AAED,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC3B,wBAAwB;IACxB,OAAO,EAAE,CAAC,CAAC;IACX,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,mCAAmC;IACnC,UAAU,EAAE,WAAW,GAAG,QAAQ,CAAC;IACnC,+CAA+C;IAC/C,KAAK,EAAE,aAAa,CAAC;IACrB,4DAA4D;IAC5D,aAAa,EAAE,aAAa,CAAC;IAC7B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,MAAM,EAAE,cAAc,CAAC;IACvB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xeonr/renderer-sdk",
3
- "version": "1.0.5",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "license": "ISC",
6
6
  "description": "SDK for building custom renderers for upl.im integrations",
@@ -15,6 +15,10 @@
15
15
  "default": "./dist/react/index.js",
16
16
  "types": "./dist/react/index.d.ts"
17
17
  },
18
+ "./react/dom": {
19
+ "default": "./dist/react/dom.js",
20
+ "types": "./dist/react/dom.d.ts"
21
+ },
18
22
  "./protocol": {
19
23
  "default": "./dist/protocol.js",
20
24
  "types": "./dist/protocol.d.ts"
@@ -25,16 +29,22 @@
25
29
  }
26
30
  },
27
31
  "peerDependencies": {
28
- "react": ">=18.0.0"
32
+ "react": ">=18.0.0",
33
+ "react-dom": ">=18.0.0"
29
34
  },
30
35
  "peerDependenciesMeta": {
31
36
  "react": {
32
37
  "optional": true
38
+ },
39
+ "react-dom": {
40
+ "optional": true
33
41
  }
34
42
  },
35
43
  "devDependencies": {
36
44
  "@types/react": "^19.0.0",
45
+ "@types/react-dom": "^19.0.0",
37
46
  "react": "^19.0.0",
47
+ "react-dom": "^19.0.0",
38
48
  "typescript": "^5.8.3"
39
49
  },
40
50
  "scripts": {