@pulse-editor/react-api 0.1.1-beta.83 → 0.1.1-beta.84
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/dist/hooks/editor/use-app-settings.d.ts +13 -0
- package/dist/hooks/editor/use-oauth.d.ts +36 -0
- package/dist/hooks/editor/use-snapshot-state.d.ts +9 -1
- package/dist/main.d.ts +5 -2
- package/dist/main.js +210 -36
- package/dist/main.js.map +1 -1
- package/dist/pulse-editor-react-api-0.1.1-beta.84.tgz +0 -0
- package/package.json +2 -2
- package/dist/pulse-editor-react-api-0.1.1-beta.83.tgz +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to access and update persisted settings for a specific app in the editor.
|
|
3
|
+
* @param appId The ID of the app whose settings should be retrieved and updated.
|
|
4
|
+
* @returns `isReady`, the current `settings` object, and an `updateSettings` function.
|
|
5
|
+
*/
|
|
6
|
+
export default function useAppSettings(appId: string): {
|
|
7
|
+
isReady: boolean;
|
|
8
|
+
isLoaded: boolean;
|
|
9
|
+
settings: Record<string, any>;
|
|
10
|
+
refetch: () => Promise<void>;
|
|
11
|
+
updateSettings: (newSettings: Record<string, any>) => Promise<void>;
|
|
12
|
+
deleteSetting: (key: string) => Promise<void>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { OAuthConnectConfig } from "@pulse-editor/shared-utils";
|
|
2
|
+
export type OAuthState = {
|
|
3
|
+
accessToken?: string;
|
|
4
|
+
refreshToken?: string;
|
|
5
|
+
tokenType?: string;
|
|
6
|
+
scope?: string;
|
|
7
|
+
expiresAt?: string | number;
|
|
8
|
+
idToken?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Hook to manage OAuth credentials for a Pulse App.
|
|
13
|
+
*
|
|
14
|
+
* Handles the full OAuth flow automatically:
|
|
15
|
+
* - Dynamic client registration (RFC 7591) when `registrationEndpoint` is provided
|
|
16
|
+
* - PKCE code challenge generation
|
|
17
|
+
* - Opening the authorization modal
|
|
18
|
+
* - Token exchange and storage (via the Pulse Editor backend)
|
|
19
|
+
*
|
|
20
|
+
* @param appId The ID of the app.
|
|
21
|
+
* @param provider A provider identifier used to namespace the OAuth state.
|
|
22
|
+
*/
|
|
23
|
+
export default function useOAuth(appId: string, provider?: string): {
|
|
24
|
+
isReady: boolean;
|
|
25
|
+
isLoading: boolean;
|
|
26
|
+
oauth: OAuthState | null;
|
|
27
|
+
isAuthenticated: boolean;
|
|
28
|
+
connect: (config: OAuthConnectConfig) => Promise<void>;
|
|
29
|
+
disconnect: () => Promise<void>;
|
|
30
|
+
refetchOAuth: () => Promise<void>;
|
|
31
|
+
refreshToken: (params: {
|
|
32
|
+
tokenEndpoint: string;
|
|
33
|
+
clientId: string;
|
|
34
|
+
clientSecret?: string;
|
|
35
|
+
}) => Promise<OAuthState | null>;
|
|
36
|
+
};
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A hook that syncs state with a snapshot context,
|
|
3
|
+
* allowing for state restoration across component unmounts and remounts.
|
|
4
|
+
* @param key
|
|
5
|
+
* @param initialValue
|
|
6
|
+
* @param onRestore
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export default function useSnapshotState<T>(key: string, initialValue?: T, onRestore?: (value: T) => void): readonly [T, import("react").Dispatch<import("react").SetStateAction<T>>];
|
package/dist/main.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import useAgentTools from "./hooks/agent/use-agent-tools";
|
|
2
2
|
import useAgents from "./hooks/agent/use-agents";
|
|
3
|
+
import useActionEffect from "./hooks/editor/use-action-effect";
|
|
3
4
|
import useLoading from "./hooks/editor/use-loading";
|
|
4
5
|
import useNotification from "./hooks/editor/use-notification";
|
|
5
|
-
import useActionEffect from "./hooks/editor/use-action-effect";
|
|
6
6
|
import useTheme from "./hooks/editor/use-theme";
|
|
7
7
|
import useFile from "./hooks/workspace/use-file";
|
|
8
8
|
import useImageGen from "./hooks/ai-modality/use-image-gen";
|
|
@@ -11,11 +11,14 @@ import useOCR from "./hooks/ai-modality/use-ocr";
|
|
|
11
11
|
import useSTT from "./hooks/ai-modality/use-stt";
|
|
12
12
|
import useTTS from "./hooks/ai-modality/use-tts";
|
|
13
13
|
import useVideoGen from "./hooks/ai-modality/use-video-gen";
|
|
14
|
+
import useAppSettings from "./hooks/editor/use-app-settings";
|
|
14
15
|
import { useArtifact } from "./hooks/editor/use-artifact";
|
|
15
16
|
import useEditorEnv from "./hooks/editor/use-editor-env";
|
|
16
17
|
import useOpenApp from "./hooks/editor/use-open-app";
|
|
17
18
|
import useOpenLink from "./hooks/editor/use-open-link";
|
|
18
19
|
import useOwnedAppView from "./hooks/editor/use-owned-app-view";
|
|
20
|
+
import useOAuth from "./hooks/editor/use-oauth";
|
|
21
|
+
export type { OAuthState } from "./hooks/editor/use-oauth";
|
|
19
22
|
import useSnapshotState from "./hooks/editor/use-snapshot-state";
|
|
20
23
|
import { useTranslations } from "./hooks/editor/use-translations";
|
|
21
24
|
import useFileSystem from "./hooks/workspace/use-file-system";
|
|
@@ -24,4 +27,4 @@ import useTerminal from "./hooks/workspace/use-terminal";
|
|
|
24
27
|
import useWorkspaceInfo from "./hooks/workspace/use-workspace-info";
|
|
25
28
|
import ReceiveFileProvider from "./providers/receive-file-provider";
|
|
26
29
|
import SnapshotProvider from "./providers/snapshot-provider";
|
|
27
|
-
export { ReceiveFileProvider, SnapshotProvider, useAgents, useAgentTools, useArtifact, useEditorEnv, useFile, useFileSystem, useImageGen, useLLM, useLoading, useNotification, useOCR, useOpenApp, useOpenLink, useOwnedAppView, useReceiveFile,
|
|
30
|
+
export { ReceiveFileProvider, SnapshotProvider, useActionEffect, useAgents, useAgentTools, useAppSettings, useArtifact, useEditorEnv, useFile, useFileSystem, useImageGen, useLLM, useLoading, useNotification, useOCR, useOpenApp, useOpenLink, useOAuth, useOwnedAppView, useReceiveFile, useSnapshotState, useSTT, useTerminal, useTheme, useTranslations, useTTS, useVideoGen, useWorkspaceInfo, };
|
package/dist/main.js
CHANGED
|
@@ -492,40 +492,6 @@ function useAgents() {
|
|
|
492
492
|
};
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
-
function useLoading() {
|
|
496
|
-
const receiverHandlerMap = new Map();
|
|
497
|
-
const { imc, isReady } = useIMC(receiverHandlerMap, "loading");
|
|
498
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
499
|
-
useEffect(() => {
|
|
500
|
-
if (isReady) {
|
|
501
|
-
imc?.sendMessage(IMCMessageTypeEnum.EditorLoadingApp, {
|
|
502
|
-
isLoading,
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
}, [isLoading]);
|
|
506
|
-
function toggleLoading(isLoading) {
|
|
507
|
-
setIsLoading((prev) => isLoading);
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
isReady,
|
|
511
|
-
toggleLoading,
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function useNotification() {
|
|
516
|
-
const receiverHandlerMap = new Map();
|
|
517
|
-
const { imc, isReady } = useIMC(receiverHandlerMap, "notification");
|
|
518
|
-
function openNotification(text, type) {
|
|
519
|
-
if (isReady) {
|
|
520
|
-
imc?.sendMessage(IMCMessageTypeEnum.EditorShowNotification, {
|
|
521
|
-
text,
|
|
522
|
-
type,
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return { isReady, openNotification };
|
|
527
|
-
}
|
|
528
|
-
|
|
529
495
|
var MANIFEST_EXT = '.json';
|
|
530
496
|
var BROWSER_LOG_KEY = 'FEDERATION_DEBUG';
|
|
531
497
|
var NameTransformSymbol = {
|
|
@@ -5851,6 +5817,40 @@ function useActionEffect(params, deps, isExtReady = true) {
|
|
|
5851
5817
|
};
|
|
5852
5818
|
}
|
|
5853
5819
|
|
|
5820
|
+
function useLoading() {
|
|
5821
|
+
const receiverHandlerMap = new Map();
|
|
5822
|
+
const { imc, isReady } = useIMC(receiverHandlerMap, "loading");
|
|
5823
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
5824
|
+
useEffect(() => {
|
|
5825
|
+
if (isReady) {
|
|
5826
|
+
imc?.sendMessage(IMCMessageTypeEnum.EditorLoadingApp, {
|
|
5827
|
+
isLoading,
|
|
5828
|
+
});
|
|
5829
|
+
}
|
|
5830
|
+
}, [isLoading]);
|
|
5831
|
+
function toggleLoading(isLoading) {
|
|
5832
|
+
setIsLoading((prev) => isLoading);
|
|
5833
|
+
}
|
|
5834
|
+
return {
|
|
5835
|
+
isReady,
|
|
5836
|
+
toggleLoading,
|
|
5837
|
+
};
|
|
5838
|
+
}
|
|
5839
|
+
|
|
5840
|
+
function useNotification() {
|
|
5841
|
+
const receiverHandlerMap = new Map();
|
|
5842
|
+
const { imc, isReady } = useIMC(receiverHandlerMap, "notification");
|
|
5843
|
+
function openNotification(text, type) {
|
|
5844
|
+
if (isReady) {
|
|
5845
|
+
imc?.sendMessage(IMCMessageTypeEnum.EditorShowNotification, {
|
|
5846
|
+
text,
|
|
5847
|
+
type,
|
|
5848
|
+
});
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
return { isReady, openNotification };
|
|
5852
|
+
}
|
|
5853
|
+
|
|
5854
5854
|
function useTheme() {
|
|
5855
5855
|
const [theme, setTheme] = useState("light");
|
|
5856
5856
|
const receiverHandlerMap = new Map();
|
|
@@ -6182,6 +6182,54 @@ function useVideoGen() {
|
|
|
6182
6182
|
};
|
|
6183
6183
|
}
|
|
6184
6184
|
|
|
6185
|
+
/**
|
|
6186
|
+
* Hook to access and update persisted settings for a specific app in the editor.
|
|
6187
|
+
* @param appId The ID of the app whose settings should be retrieved and updated.
|
|
6188
|
+
* @returns `isReady`, the current `settings` object, and an `updateSettings` function.
|
|
6189
|
+
*/
|
|
6190
|
+
function useAppSettings(appId) {
|
|
6191
|
+
const receiverHandlerMap = new Map();
|
|
6192
|
+
const { imc, isReady } = useIMC(receiverHandlerMap, "app-settings");
|
|
6193
|
+
const [settings, setSettings] = useState({});
|
|
6194
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
6195
|
+
useEffect(() => {
|
|
6196
|
+
if (isReady) {
|
|
6197
|
+
refetch();
|
|
6198
|
+
}
|
|
6199
|
+
}, [isReady]);
|
|
6200
|
+
async function refetch() {
|
|
6201
|
+
const result = await imc?.sendMessage(IMCMessageTypeEnum.EditorGetAppSettings, { appId });
|
|
6202
|
+
setSettings(result ?? {});
|
|
6203
|
+
setIsLoaded(true);
|
|
6204
|
+
}
|
|
6205
|
+
async function updateSettings(newSettings) {
|
|
6206
|
+
await imc?.sendMessage(IMCMessageTypeEnum.EditorSetAppSettings, {
|
|
6207
|
+
appId,
|
|
6208
|
+
settings: newSettings,
|
|
6209
|
+
});
|
|
6210
|
+
setSettings((prev) => ({ ...prev, ...newSettings }));
|
|
6211
|
+
}
|
|
6212
|
+
async function deleteSetting(key) {
|
|
6213
|
+
await imc?.sendMessage(IMCMessageTypeEnum.EditorDeleteAppSetting, {
|
|
6214
|
+
appId,
|
|
6215
|
+
key,
|
|
6216
|
+
});
|
|
6217
|
+
setSettings((prev) => {
|
|
6218
|
+
const next = { ...prev };
|
|
6219
|
+
delete next[key];
|
|
6220
|
+
return next;
|
|
6221
|
+
});
|
|
6222
|
+
}
|
|
6223
|
+
return {
|
|
6224
|
+
isReady,
|
|
6225
|
+
isLoaded,
|
|
6226
|
+
settings,
|
|
6227
|
+
refetch,
|
|
6228
|
+
updateSettings,
|
|
6229
|
+
deleteSetting,
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
|
|
6185
6233
|
function useArtifact() {
|
|
6186
6234
|
const receiverHandlerMap = new Map();
|
|
6187
6235
|
const { imc, isReady } = useIMC(receiverHandlerMap, "artifact");
|
|
@@ -6285,6 +6333,124 @@ function useOwnedAppView() {
|
|
|
6285
6333
|
};
|
|
6286
6334
|
}
|
|
6287
6335
|
|
|
6336
|
+
function generateRandomString(length) {
|
|
6337
|
+
const array = new Uint8Array(length);
|
|
6338
|
+
crypto.getRandomValues(array);
|
|
6339
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
6340
|
+
}
|
|
6341
|
+
/**
|
|
6342
|
+
* Hook to manage OAuth credentials for a Pulse App.
|
|
6343
|
+
*
|
|
6344
|
+
* Handles the full OAuth flow automatically:
|
|
6345
|
+
* - Dynamic client registration (RFC 7591) when `registrationEndpoint` is provided
|
|
6346
|
+
* - PKCE code challenge generation
|
|
6347
|
+
* - Opening the authorization modal
|
|
6348
|
+
* - Token exchange and storage (via the Pulse Editor backend)
|
|
6349
|
+
*
|
|
6350
|
+
* @param appId The ID of the app.
|
|
6351
|
+
* @param provider A provider identifier used to namespace the OAuth state.
|
|
6352
|
+
*/
|
|
6353
|
+
function useOAuth(appId, provider = "default") {
|
|
6354
|
+
const { isReady, isLoaded, settings, deleteSetting, refetch } = useAppSettings(appId);
|
|
6355
|
+
const keyPrefix = `oauth:${provider}:`;
|
|
6356
|
+
const oauthEntries = Object.entries(settings).filter(([k]) => k.startsWith(keyPrefix));
|
|
6357
|
+
const oauthRaw = oauthEntries.length > 0
|
|
6358
|
+
? Object.fromEntries(oauthEntries.map(([k, v]) => [k.slice(keyPrefix.length), v]))
|
|
6359
|
+
: null;
|
|
6360
|
+
const oauth = oauthRaw &&
|
|
6361
|
+
Object.values(oauthRaw).some((v) => v !== null && v !== undefined)
|
|
6362
|
+
? oauthRaw
|
|
6363
|
+
: null;
|
|
6364
|
+
const receiverHandlerMap = new Map();
|
|
6365
|
+
const { imc, isReady: isIMCReady } = useIMC(receiverHandlerMap, "oauth");
|
|
6366
|
+
/**
|
|
6367
|
+
* Initiate the OAuth flow. The editor handles everything:
|
|
6368
|
+
* - Dynamic client registration (if `registrationEndpoint` is provided and `clientId` is omitted)
|
|
6369
|
+
* - PKCE (`codeVerifier` is auto-generated if not provided)
|
|
6370
|
+
* - Opening the consent modal and authorization window
|
|
6371
|
+
* - Token exchange and storage
|
|
6372
|
+
*/
|
|
6373
|
+
async function connect(config) {
|
|
6374
|
+
if (!isIMCReady) {
|
|
6375
|
+
throw new Error("IMC is not ready.");
|
|
6376
|
+
}
|
|
6377
|
+
// Auto-generate PKCE code verifier if not provided
|
|
6378
|
+
const resolvedConfig = { ...config };
|
|
6379
|
+
if (!resolvedConfig.codeVerifier) {
|
|
6380
|
+
resolvedConfig.codeVerifier = generateRandomString(64);
|
|
6381
|
+
resolvedConfig.codeChallengeMethod =
|
|
6382
|
+
resolvedConfig.codeChallengeMethod ?? "S256";
|
|
6383
|
+
}
|
|
6384
|
+
await imc?.sendMessage(IMCMessageTypeEnum.EditorOAuthConnect, {
|
|
6385
|
+
appId,
|
|
6386
|
+
provider,
|
|
6387
|
+
config: resolvedConfig,
|
|
6388
|
+
});
|
|
6389
|
+
}
|
|
6390
|
+
/**
|
|
6391
|
+
* Sign out: removes all stored OAuth tokens for this provider from the
|
|
6392
|
+
* editor backend and resets local auth state immediately.
|
|
6393
|
+
*/
|
|
6394
|
+
async function disconnect() {
|
|
6395
|
+
// Collect keys before deletion so we don't rely on stale `oauth`
|
|
6396
|
+
const keysToDelete = oauthEntries.map(([k]) => k);
|
|
6397
|
+
await Promise.all(keysToDelete.map((key) => deleteSetting(key)));
|
|
6398
|
+
}
|
|
6399
|
+
/**
|
|
6400
|
+
* Re-fetch OAuth state from app settings.
|
|
6401
|
+
* Call this after the user completes the OAuth flow in the external window.
|
|
6402
|
+
*/
|
|
6403
|
+
async function refetchOAuth() {
|
|
6404
|
+
await refetch();
|
|
6405
|
+
}
|
|
6406
|
+
// Revalidate OAuth state when the user returns to the tab
|
|
6407
|
+
useEffect(() => {
|
|
6408
|
+
function handleVisibilityChange() {
|
|
6409
|
+
if (document.visibilityState === "visible") {
|
|
6410
|
+
refetch();
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
6414
|
+
return () => {
|
|
6415
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
6416
|
+
};
|
|
6417
|
+
}, [isReady]);
|
|
6418
|
+
/**
|
|
6419
|
+
* Refresh the access token using the stored refresh token.
|
|
6420
|
+
* The editor backend exchanges the refresh token with the OAuth provider
|
|
6421
|
+
* and persists the new tokens automatically.
|
|
6422
|
+
*/
|
|
6423
|
+
async function refreshToken(params) {
|
|
6424
|
+
if (!isIMCReady) {
|
|
6425
|
+
throw new Error("IMC is not ready.");
|
|
6426
|
+
}
|
|
6427
|
+
if (!oauth?.refreshToken) {
|
|
6428
|
+
throw new Error("No refresh token available.");
|
|
6429
|
+
}
|
|
6430
|
+
const result = await imc?.sendMessage(IMCMessageTypeEnum.EditorOAuthRefreshToken, {
|
|
6431
|
+
appId,
|
|
6432
|
+
provider,
|
|
6433
|
+
tokenEndpoint: params.tokenEndpoint,
|
|
6434
|
+
refreshToken: oauth.refreshToken,
|
|
6435
|
+
clientId: params.clientId,
|
|
6436
|
+
clientSecret: params.clientSecret,
|
|
6437
|
+
});
|
|
6438
|
+
// Re-fetch settings to sync local state
|
|
6439
|
+
await refetchOAuth();
|
|
6440
|
+
return result ?? null;
|
|
6441
|
+
}
|
|
6442
|
+
return {
|
|
6443
|
+
isReady,
|
|
6444
|
+
isLoading: !isLoaded,
|
|
6445
|
+
oauth,
|
|
6446
|
+
isAuthenticated: Boolean(oauth?.accessToken),
|
|
6447
|
+
connect,
|
|
6448
|
+
disconnect,
|
|
6449
|
+
refetchOAuth,
|
|
6450
|
+
refreshToken,
|
|
6451
|
+
};
|
|
6452
|
+
}
|
|
6453
|
+
|
|
6288
6454
|
const SnapshotContext = createContext(undefined);
|
|
6289
6455
|
function SnapshotProvider({ children, }) {
|
|
6290
6456
|
const [states, setStates] = useState({});
|
|
@@ -6315,7 +6481,15 @@ function SnapshotProvider({ children, }) {
|
|
|
6315
6481
|
return (React.createElement(SnapshotContext.Provider, { value: { states, setStates } }, children));
|
|
6316
6482
|
}
|
|
6317
6483
|
|
|
6318
|
-
|
|
6484
|
+
/**
|
|
6485
|
+
* A hook that syncs state with a snapshot context,
|
|
6486
|
+
* allowing for state restoration across component unmounts and remounts.
|
|
6487
|
+
* @param key
|
|
6488
|
+
* @param initialValue
|
|
6489
|
+
* @param onRestore
|
|
6490
|
+
* @returns
|
|
6491
|
+
*/
|
|
6492
|
+
function useSnapshotState(key, initialValue, onRestore) {
|
|
6319
6493
|
const snapshotContext = useContext(SnapshotContext);
|
|
6320
6494
|
if (!snapshotContext) {
|
|
6321
6495
|
throw new Error("useSnapShotState must be used within a SnapshotProvider");
|
|
@@ -6543,5 +6717,5 @@ function useWorkspaceInfo() {
|
|
|
6543
6717
|
};
|
|
6544
6718
|
}
|
|
6545
6719
|
|
|
6546
|
-
export { ReceiveFileProvider, SnapshotProvider, useActionEffect, useAgentTools, useAgents, useArtifact, useEditorEnv, useFile, useFileSystem, useImageGen, useLLM, useLoading, useNotification, useOCR, useOpenApp, useOpenLink, useOwnedAppView, useReceiveFile, useSTT,
|
|
6720
|
+
export { ReceiveFileProvider, SnapshotProvider, useActionEffect, useAgentTools, useAgents, useAppSettings, useArtifact, useEditorEnv, useFile, useFileSystem, useImageGen, useLLM, useLoading, useNotification, useOAuth, useOCR, useOpenApp, useOpenLink, useOwnedAppView, useReceiveFile, useSTT, useSnapshotState, useTTS, useTerminal, useTheme, useTranslations, useVideoGen, useWorkspaceInfo };
|
|
6547
6721
|
//# sourceMappingURL=main.js.map
|