@xeonr/renderer-sdk 1.5.0 → 1.5.2

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.
@@ -1,250 +0,0 @@
1
- import { useState, useEffect, useMemo } from 'react';
2
- import { RendererClient } from '../client.js';
3
- import type { RendererClientOptions } from '../client.js';
4
- import type { RendererApiAdapter, RendererConfig, RendererScope, RenderingType, InitPayload } from '../types.js';
5
-
6
- export interface UseRendererClientOptions extends RendererClientOptions {}
7
-
8
- // One RendererClient per page. Multiple `useRendererClient()` calls
9
- // (different components subscribing to the same state) all share the
10
- // singleton — previously each call spawned its own client, which
11
- // caused duplicate `window.error` / `unhandledrejection` listeners
12
- // (so every crash got reported N times), duplicate `uplim:ready`
13
- // pings, and state divergence between instances.
14
- //
15
- // Page lifetime: the iframe is full-reloaded for HMR / test, so the
16
- // module-level singleton lives exactly as long as it should. SDK-level
17
- // HMR (developer working on the SDK in monorepo, hot-replacing this
18
- // module) is handled via `import.meta.hot.dispose` at the bottom of
19
- // the file.
20
- let sharedClient: RendererClient | null = null;
21
- let sharedClientOptionsKey: string | null = null;
22
-
23
- function optionsKey(options: UseRendererClientOptions | undefined): string {
24
- if (!options) return '';
25
- // Stable order so { a, b } and { b, a } produce the same key. Used
26
- // only for the dev-mode mismatch warning — never for behavior.
27
- const entries = Object.entries(options).sort(([a], [b]) => a.localeCompare(b));
28
- return JSON.stringify(entries);
29
- }
30
-
31
- function getSharedClient(options: UseRendererClientOptions | undefined): RendererClient {
32
- if (sharedClient) {
33
- const key = optionsKey(options);
34
- // Best-effort dev nudge: if a later mount passes different
35
- // options than the first one, those options are silently
36
- // ignored. Only flag when keys differ AND the later caller
37
- // passed non-empty options (passing nothing is the common
38
- // case for downstream subscribers).
39
- if (key && sharedClientOptionsKey !== null && key !== sharedClientOptionsKey) {
40
- // eslint-disable-next-line no-console
41
- console.warn(
42
- '[uplim] useRendererClient called with different options after first call; ' +
43
- 'later options are ignored (the client is a per-page singleton).',
44
- );
45
- }
46
- return sharedClient;
47
- }
48
- sharedClient = new RendererClient(options);
49
- sharedClientOptionsKey = optionsKey(options);
50
- return sharedClient;
51
- }
52
-
53
- // SDK-dev HMR cleanup: when this module is hot-replaced (e.g. someone
54
- // editing the SDK with the renderer harness running), detach the old
55
- // client's listeners before the new module evaluates. Prod / non-HMR
56
- // builds skip the entire block — `import.meta.hot` is undefined.
57
- const maybeHot = (import.meta as ImportMeta & { hot?: { dispose: (cb: () => void) => void } }).hot;
58
- if (maybeHot) {
59
- maybeHot.dispose(() => {
60
- if (sharedClient) {
61
- sharedClient.destroy();
62
- sharedClient = null;
63
- sharedClientOptionsKey = null;
64
- }
65
- });
66
- }
67
-
68
- /**
69
- * Apply the host's theme to the renderer document. Two distinct effects:
70
- *
71
- * - `documentElement.dataset.theme` — surfaces the theme as a
72
- * `[data-theme="dark"]` selector for renderer-authored CSS.
73
- * - `documentElement.style.colorScheme` — tells the browser to render
74
- * scrollbars, form controls, and (critically) the iframe's default
75
- * canvas in the matching mode. Without this, a cross-origin iframe
76
- * defaults to a white canvas regardless of what the host's
77
- * `color-scheme` is — so `background: transparent` in the renderer
78
- * reveals white, not the host's dark surface. This is the fix for
79
- * "renderer shows light mode when host is dark".
80
- */
81
- function applyThemeToDocument(theme: 'light' | 'dark'): void {
82
- if (typeof document === 'undefined') return;
83
- document.documentElement.dataset.theme = theme;
84
- document.documentElement.style.colorScheme = theme;
85
- }
86
-
87
- export interface UseRendererClientResult {
88
- /** Whether the host has sent the init message. */
89
- connected: boolean;
90
- /** What resource the renderer is scoped to. */
91
- scope: RendererScope | null;
92
- /** How the renderer is being displayed. */
93
- renderingType: RenderingType | null;
94
- /** The current integration access token. */
95
- token: string | null;
96
- /** Unix timestamp (ms) when the token expires. */
97
- tokenExpiresAt: number | null;
98
- /** The current host theme. */
99
- theme: 'light' | 'dark';
100
- /** The parsed config.json from the renderer archive. */
101
- config: RendererConfig | null;
102
- /** Which entrypoint was loaded (dashboard or portal). */
103
- entrypoint: 'dashboard' | 'portal' | null;
104
- /** Base URL for upl.im API calls. */
105
- apiBaseUrl: string | null;
106
- /** Pre-configured API adapter for use with `getUploadClientWithEnv()`. */
107
- apiAdapter: RendererApiAdapter;
108
- /**
109
- * Pre-loaded resource snapshot (typically the Upload proto's JSON
110
- * form for upload-scoped renderers). Renderers should cast to the
111
- * expected proto JSON type and use this directly to avoid an extra
112
- * GetUpload round trip on first paint. `null` when the host didn't
113
- * supply one (older hosts, folder/virtual-file scopes); fall back
114
- * to a fetch in that case.
115
- */
116
- resource: unknown;
117
- /** Current hash path within the renderer (e.g. '/settings'). Updated reactively on hash changes. */
118
- path: string;
119
-
120
- /** Request the host to open a specific upload. */
121
- openUpload: (uploadId: string) => void;
122
- /** Request a fresh integration access token. */
123
- requestToken: () => void;
124
- /** Request the host to close this renderer (modal mode). */
125
- close: () => void;
126
- /**
127
- * When the renderer's `config.json` has `deferReady: true`, call
128
- * this once the renderer's data is loaded and the UI is ready to
129
- * be visible. The host overlay stays up until this fires (or until
130
- * the SDK's safety timeout elapses — see
131
- * `RendererClientOptions.readyTimeoutMs`). No-op when `deferReady`
132
- * isn't set in config.
133
- */
134
- markReady: () => void;
135
-
136
- /** The underlying RendererClient instance for advanced use. */
137
- client: RendererClient;
138
- }
139
-
140
- /**
141
- * React hook for building custom renderers.
142
- *
143
- * Creates a `RendererClient` instance, manages its lifecycle, and exposes
144
- * reactive state that triggers re-renders when the host sends messages.
145
- *
146
- * ```tsx
147
- * import { useRendererClient } from '@xeonr/renderer-sdk/react';
148
- *
149
- * function App() {
150
- * const { connected, scope, renderingType, theme, token } = useRendererClient();
151
- *
152
- * if (!connected) return <div>Connecting...</div>;
153
- *
154
- * return (
155
- * <div data-theme={theme}>
156
- * <pre>{JSON.stringify(scope, null, 2)}</pre>
157
- * </div>
158
- * );
159
- * }
160
- * ```
161
- */
162
- export function useRendererClient(options?: UseRendererClientOptions): UseRendererClientResult {
163
- const client = getSharedClient(options);
164
-
165
- // Initial state read directly from the client — handles late
166
- // subscribers (a second `useRendererClient` mount that happens
167
- // after init has already fired). Without this, the late mount
168
- // would sit at `connected: false` forever because onInit only
169
- // fires for NEW init events, not for already-received state.
170
- const [connected, setConnected] = useState<boolean>(() => client.isConnected());
171
- const [scope, setScope] = useState<RendererScope | null>(() => client.getScope());
172
- const [renderingType, setRenderingType] = useState<RenderingType | null>(() => client.getRenderingType());
173
- const [token, setToken] = useState<string | null>(() => client.getToken());
174
- const [tokenExpiresAt, setTokenExpiresAt] = useState<number | null>(() => client.getTokenExpiresAt());
175
- const [theme, setTheme] = useState<'light' | 'dark'>(() => client.getTheme());
176
- const [config, setConfig] = useState<RendererConfig | null>(() => client.getConfig());
177
- const [entrypoint, setEntrypoint] = useState<'dashboard' | 'portal' | null>(() => client.getEntrypoint());
178
- const [apiBaseUrl, setApiBaseUrl] = useState<string | null>(() => client.getApiBaseUrl());
179
- const [resource, setResource] = useState<unknown>(() => client.getResource());
180
- const [path, setPath] = useState<string>(() => client.getPath());
181
-
182
- useEffect(() => {
183
- const unsubInit = client.onInit((payload: InitPayload) => {
184
- setConnected(true);
185
- setScope(payload.scope);
186
- setRenderingType(payload.renderingType);
187
- setToken(payload.token);
188
- setTokenExpiresAt(payload.tokenExpiresAt);
189
- setTheme(payload.theme);
190
- setConfig(payload.config);
191
- setEntrypoint(payload.entrypoint);
192
- setApiBaseUrl(payload.apiBaseUrl);
193
- setResource(payload.resource ?? null);
194
- setPath(client.getPath());
195
- applyThemeToDocument(payload.theme);
196
- });
197
-
198
- const unsubTheme = client.onThemeChange((newTheme) => {
199
- setTheme(newTheme);
200
- applyThemeToDocument(newTheme);
201
- });
202
-
203
- const unsubToken = client.onTokenRefresh((newToken, newExpiresAt) => {
204
- setToken(newToken);
205
- setTokenExpiresAt(newExpiresAt);
206
- });
207
-
208
- const unsubNavigate = client.onNavigate((newPath) => {
209
- setPath(newPath);
210
- });
211
-
212
- return () => {
213
- unsubInit();
214
- unsubTheme();
215
- unsubToken();
216
- unsubNavigate();
217
- // NB: do NOT destroy() here — the client is a per-page
218
- // singleton, not a per-component instance. Component
219
- // unmounts (route changes / strict-mode double-mount /
220
- // React render-time tearing) shouldn't kill the shared
221
- // state. The iframe full-reload tears the singleton down
222
- // implicitly when the page goes away; SDK-level HMR is
223
- // handled by import.meta.hot.dispose at the top of file.
224
- };
225
- }, [client]);
226
-
227
- const methods = useMemo(() => ({
228
- openUpload: (uploadId: string) => client.openUpload(uploadId),
229
- requestToken: () => client.requestToken(),
230
- close: () => client.close(),
231
- markReady: () => client.signalReady(),
232
- apiAdapter: client.getApiAdapter(),
233
- }), [client]);
234
-
235
- return {
236
- connected,
237
- scope,
238
- renderingType,
239
- token,
240
- tokenExpiresAt,
241
- theme,
242
- config,
243
- entrypoint,
244
- apiBaseUrl,
245
- resource,
246
- path,
247
- client,
248
- ...methods,
249
- };
250
- }
@@ -1,71 +0,0 @@
1
- import { useCallback, useState } from 'react';
2
-
3
- /**
4
- * Hook for surfacing async failures to `<RendererErrorBoundary>` (and from
5
- * there to the host's crash overlay + telemetry).
6
- *
7
- * Renderers shouldn't render their own "Upload not found" / "Failed to
8
- * load" inline UI — those are still crashes from a telemetry standpoint,
9
- * and the host already owns a polished overlay with reload + fallback
10
- * actions. This hook lets a renderer take an error caught in an async
11
- * context (fetch `.catch()`, useEffect cleanup, etc.) and route it to
12
- * the host instead of rendering a half-broken inline state.
13
- *
14
- * Pattern:
15
- *
16
- * ```tsx
17
- * const reportFatal = useReportFatalError();
18
- * useEffect(() => {
19
- * client.getUpload(...).catch(reportFatal);
20
- * }, []);
21
- * ```
22
- *
23
- * Mechanics: we keep an error in component state. When set, the *next*
24
- * render throws it — React's error-handling pipeline catches the throw
25
- * and bubbles to the nearest boundary. From there
26
- * `RendererErrorBoundary.componentDidCatch` reports the crash to the
27
- * host and renders nothing, and the host overlay (Reload / Copy /
28
- * Fallback) takes over the visible iframe area.
29
- *
30
- * Why state-then-throw rather than a direct `throw err`? You can't
31
- * throw to React from async code — React only sees throws that happen
32
- * during render or in event handlers. Routing through state ensures
33
- * the throw lands inside the render phase.
34
- *
35
- * The returned callback is stable across renders so it's safe to drop
36
- * straight into a `.catch()`.
37
- */
38
- export function useReportFatalError(): (err: unknown) => void {
39
- // Generic state slot — we never read the value, only use the
40
- // updater. We narrow to `never` so TypeScript stops us from
41
- // accidentally using it as data anywhere.
42
- const [, setState] = useState<never>();
43
- return useCallback((err: unknown) => {
44
- // Functional updater throws — React invokes it during the next
45
- // render attempt, which puts the throw inside the render phase
46
- // where boundaries can see it.
47
- setState(() => {
48
- if (err instanceof Error) {
49
- throw err;
50
- }
51
- // Wrap non-Error throws so the boundary always sees a real
52
- // Error instance with a meaningful .name / .message. Without
53
- // this, `client.reportCrash` would synthesise a generic
54
- // 'UnknownError' which loses the original payload's shape.
55
- if (typeof err === 'string') {
56
- throw new Error(err);
57
- }
58
- if (err && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
59
- const wrapped = new Error(err.message);
60
- if ('name' in err && typeof err.name === 'string') {
61
- wrapped.name = err.name;
62
- }
63
- if ('stack' in err && typeof err.stack === 'string') {
64
- wrapped.stack = err.stack;
65
- }
66
- throw wrapped;
67
- }
68
- throw new Error('Unknown renderer error');
69
- });
70
- }, []);
71
- }
package/src/resources.ts DELETED
@@ -1,119 +0,0 @@
1
- /**
2
- * Typed resource accessors that read from the host-seeded `resource`
3
- * cache first and fall back to a real API call when the cache is
4
- * absent or stale.
5
- *
6
- * Lives in its own file so the imports against `@xeonr/uploads-protocol`
7
- * and `@xeonr/uploads-sdk` are kept out of `client.ts`. Those packages
8
- * are declared as optional peer dependencies — a renderer that never
9
- * calls `getUpload()` / `getFolder()` / `getBucket()` doesn't need to
10
- * install them. The fetch helpers throw at call time when the deps are
11
- * missing.
12
- */
13
- import { fromJson } from '@bufbuild/protobuf';
14
- import {
15
- BucketUploadsService,
16
- BucketFoldersService,
17
- UploadSchema,
18
- FolderSchema,
19
- type Upload,
20
- type Folder,
21
- } from '@xeonr/uploads-protocol/uplim/api/v1/uploads_pb';
22
- import {
23
- BucketsService,
24
- BucketSchema,
25
- type Bucket,
26
- } from '@xeonr/uploads-protocol/uplim/api/v1/buckets_pb';
27
- import { getUploadClientWithEnv } from '@xeonr/uploads-sdk/api/base';
28
- import type { RendererApiAdapter, RendererScope } from './types.js';
29
-
30
- /**
31
- * Best-effort decoder for the host's pre-loaded `resource` field. The
32
- * host serialises via `toJson(UploadSchema, …)`/etc and posts the JSON
33
- * across the iframe boundary, so we run it back through `fromJson` to
34
- * land at a real Message instance with the right prototype methods.
35
- *
36
- * Returns null on any failure (schema mismatch, missing fields, etc.)
37
- * so callers fall through to a fresh fetch.
38
- */
39
- export function decodeUploadResource(value: unknown): Upload | null {
40
- if (!value || typeof value !== 'object') return null;
41
- try {
42
- return fromJson(UploadSchema, value as Parameters<typeof fromJson<typeof UploadSchema>>[1]);
43
- } catch {
44
- return null;
45
- }
46
- }
47
-
48
- export function decodeFolderResource(value: unknown): Folder | null {
49
- if (!value || typeof value !== 'object') return null;
50
- try {
51
- return fromJson(FolderSchema, value as Parameters<typeof fromJson<typeof FolderSchema>>[1]);
52
- } catch {
53
- return null;
54
- }
55
- }
56
-
57
- export function decodeBucketResource(value: unknown): Bucket | null {
58
- if (!value || typeof value !== 'object') return null;
59
- try {
60
- return fromJson(BucketSchema, value as Parameters<typeof fromJson<typeof BucketSchema>>[1]);
61
- } catch {
62
- return null;
63
- }
64
- }
65
-
66
- /**
67
- * Fetch the upload for the current scope via the API. Caller already
68
- * narrowed the scope; we just translate to the BucketUploadsService
69
- * RPC. Throws when the API returns no upload (treated as a not-found
70
- * by the renderer).
71
- */
72
- export async function fetchUpload(
73
- scope: RendererScope,
74
- apiAdapter: RendererApiAdapter,
75
- ): Promise<Upload> {
76
- if (scope.type !== 'upload') {
77
- throw new Error('fetchUpload requires an upload-typed scope');
78
- }
79
- const client = getUploadClientWithEnv(BucketUploadsService, apiAdapter);
80
- const res = await client.getUpload({
81
- bucketRef: { type: { case: 'bucketId', value: scope.bucketId } },
82
- uploadRef: { type: { case: 'uploadId', value: scope.uploadId } },
83
- });
84
- if (!res.upload) throw new Error('Upload not found');
85
- return res.upload;
86
- }
87
-
88
- export async function fetchFolder(
89
- scope: RendererScope,
90
- apiAdapter: RendererApiAdapter,
91
- ): Promise<Folder> {
92
- // Folder scopes ('folder' or 'virtual-file' with a folderId) carry
93
- // the identifier; everything else is a misuse.
94
- let folderId: string | undefined;
95
- if (scope.type === 'folder') folderId = scope.folderId;
96
- else if (scope.type === 'virtual-file') folderId = scope.folderId;
97
- if (!folderId) {
98
- throw new Error('fetchFolder requires a folder-typed scope with a folderId');
99
- }
100
- const client = getUploadClientWithEnv(BucketFoldersService, apiAdapter);
101
- const res = await client.getFolder({
102
- bucketRef: { type: { case: 'bucketId', value: scope.bucketId } },
103
- folderRef: { type: { case: 'folderId', value: folderId } },
104
- });
105
- if (!res.folder) throw new Error('Folder not found');
106
- return res.folder;
107
- }
108
-
109
- export async function fetchBucket(
110
- scope: RendererScope,
111
- apiAdapter: RendererApiAdapter,
112
- ): Promise<Bucket> {
113
- const client = getUploadClientWithEnv(BucketsService, apiAdapter);
114
- const res = await client.getBucket({
115
- bucketRef: { type: { case: 'bucketId', value: scope.bucketId } },
116
- });
117
- if (!res.bucket) throw new Error('Bucket not found');
118
- return res.bucket;
119
- }
package/src/types.ts DELETED
@@ -1,156 +0,0 @@
1
- /**
2
- * config.json schema — lives at the root of a renderer archive.
3
- * Validated server-side on upload; provided to the renderer in the init message.
4
- */
5
- export interface RendererConfig {
6
- version: 1;
7
- /** Permissions the renderer requests from the host. */
8
- permissions?: RendererPermission[];
9
- sandbox?: {
10
- allowPopups?: boolean;
11
- allowForms?: boolean;
12
- allowDownloads?: boolean;
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;
54
- }
55
-
56
- export type RendererPermission =
57
- | 'createFolder'
58
- | 'openUpload';
59
-
60
- /**
61
- * What resource the renderer is scoped to.
62
- * Sent from host to iframe in the init message.
63
- */
64
- export type RendererScope =
65
- | RendererBucketScope
66
- | RendererFolderScope
67
- | RendererUploadScope
68
- | RendererVirtualFileScope;
69
-
70
- export interface RendererBucketScope {
71
- type: 'bucket';
72
- bucketId: string;
73
- }
74
-
75
- export interface RendererFolderScope {
76
- type: 'folder';
77
- bucketId: string;
78
- folderId: string;
79
- path: string;
80
- }
81
-
82
- export interface RendererUploadScope {
83
- type: 'upload';
84
- bucketId: string;
85
- uploadId: string;
86
- }
87
-
88
- export interface RendererVirtualFileScope {
89
- type: 'virtual-file';
90
- bucketId: string;
91
- folderId?: string;
92
- path: string;
93
- }
94
-
95
- /**
96
- * How the renderer is being displayed within the dashboard entrypoint.
97
- * A single renderer may handle multiple rendering types.
98
- */
99
- export type RenderingType =
100
- | 'bucket-renderer'
101
- | 'folder-renderer'
102
- | 'preview-modal';
103
-
104
- /**
105
- * API adapter compatible with `IUploadAdapterEnv` from `@xeonr/uploads-sdk`.
106
- * Pass this to `getUploadClientWithEnv()` to get a pre-configured API client
107
- * that handles authentication and token refresh automatically.
108
- */
109
- export interface RendererApiAdapter {
110
- hostname?: string;
111
- tokenHelper?: () => Promise<string | null>;
112
- onAuthenticationExpired?: (retryCount: number) => Promise<boolean>;
113
- onAuthenticationFailed?: () => Promise<any>;
114
- }
115
-
116
- /** Payload received by the renderer on initialization. */
117
- export interface InitPayload {
118
- /** Protocol version. */
119
- version: 1;
120
- /** Integration access token scoped to the integration's client. */
121
- token: string;
122
- /** Unix timestamp (ms) when the token expires. */
123
- tokenExpiresAt: number;
124
- /** Current host theme. */
125
- theme: 'light' | 'dark';
126
- /** Which entrypoint was loaded. */
127
- entrypoint: 'dashboard' | 'portal';
128
- /** What resource the renderer is scoped to. */
129
- scope: RendererScope;
130
- /** How the renderer is being displayed (dashboard only). */
131
- renderingType: RenderingType;
132
- /** Base URL for upl.im API calls. */
133
- apiBaseUrl: string;
134
- /** The parsed config.json from the renderer archive. */
135
- config: RendererConfig;
136
- /** Initial hash path to navigate to, derived from the host URL fragment. */
137
- initialPath?: string;
138
- /**
139
- * Pre-loaded resource snapshot the host already had on hand when it
140
- * mounted the iframe — typically the `Upload` proto (in its JSON
141
- * encoding) for upload-scoped renderers, or `Folder` for folder
142
- * scopes. Renderers can consume this directly to avoid an extra
143
- * GetUpload / GetFolder round trip on first paint.
144
- *
145
- * Shape is intentionally loose (`unknown`) because the SDK is
146
- * deliberately decoupled from the @xeonr/uploads-protocol types —
147
- * renderers cast to the proto JSON type (e.g. `UploadJson`) on use.
148
- * Treat as advisory: if absent or stale, fall back to a fresh fetch.
149
- *
150
- * Stays in sync via the existing `resource_url`-style fetches that
151
- * the renderer triggers when the host posts an updated init (e.g.
152
- * after edit / replace flows). For now hosts only populate the
153
- * field for upload-typed scopes; future hosts may extend.
154
- */
155
- resource?: unknown;
156
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "sourceMap": true,
9
- "outDir": "./dist",
10
- "rootDir": "./src",
11
- "jsx": "react-jsx",
12
- "strict": true,
13
- "esModuleInterop": true,
14
- "skipLibCheck": true,
15
- "forceConsistentCasingInFileNames": true
16
- },
17
- "include": ["src"]
18
- }
@@ -1 +0,0 @@
1
- {"root":["./src/client.ts","./src/index.ts","./src/protocol.ts","./src/types.ts","./src/react/index.ts","./src/react/userendererclient.ts"],"version":"5.9.3"}