@keboola/api-client-react 20.0.0 → 21.0.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.
- package/README.md +16 -15
- package/dist/index.cjs +110 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -8
- package/dist/index.d.ts +183 -8
- package/dist/index.js +108 -19
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -14,39 +14,40 @@ Peer dependencies: `react ^19.0.0`, `@keboola/api-client`.
|
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
A keboola client is `{ api, session }`. Mount the auth provider (it drives the `session` to ready and renders `fallback` until then) with `ApiClientProvider` inside it (it propagates `api` to `useApiClient`). Legacy (SAPI) shown here; bearer uses `createKeboola` + `AuthProvider`.
|
|
18
18
|
|
|
19
19
|
```tsx
|
|
20
|
-
import {
|
|
21
|
-
import { ApiClientProvider, useApiClient } from '@keboola/api-client-react';
|
|
20
|
+
import { createLegacyKeboola } from '@keboola/api-client';
|
|
21
|
+
import { ApiClientProvider, LegacyAuthProvider, useApiClient } from '@keboola/api-client-react';
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const { api, session } = createLegacyKeboola();
|
|
24
24
|
|
|
25
25
|
export const App = () => (
|
|
26
|
-
<
|
|
27
|
-
|
|
26
|
+
<LegacyAuthProvider
|
|
27
|
+
session={session}
|
|
28
28
|
baseUrl="https://connection.keboola.com"
|
|
29
|
-
|
|
30
|
-
metastoreBaseUrl="https://metastore.keboola.com"
|
|
31
|
-
onError={(error) => console.error('API Error:', error)}
|
|
29
|
+
storageToken="your-storage-token"
|
|
32
30
|
fallback={<div>Loading…</div>}
|
|
33
31
|
>
|
|
34
|
-
<
|
|
35
|
-
|
|
32
|
+
<ApiClientProvider api={api}>
|
|
33
|
+
<Tables />
|
|
34
|
+
</ApiClientProvider>
|
|
35
|
+
</LegacyAuthProvider>
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
const Tables = () => {
|
|
39
39
|
const api = useApiClient();
|
|
40
|
-
// api.storage.tables.
|
|
40
|
+
// api.storage.tables.getTables(), api.queue.searchJobs(), etc.
|
|
41
41
|
return null;
|
|
42
42
|
};
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Exported hooks
|
|
46
46
|
|
|
47
|
-
- `useApiClient()` — returns the
|
|
48
|
-
- `useProject()` — returns `
|
|
49
|
-
- `useStack()` — returns `
|
|
47
|
+
- `useApiClient()` — returns the `api` (product surface: `auth` + `management` + product namespaces).
|
|
48
|
+
- `useProject()` — returns `api.context.project` (project features + backend availability).
|
|
49
|
+
- `useStack()` — returns `api.context.stack` (service discovery + feature flags).
|
|
50
|
+
- `useLegacyAuth()` / `useAuth()` — the flow-specific `session` (legacy / bearer).
|
|
50
51
|
|
|
51
52
|
## Ownership
|
|
52
53
|
|
package/dist/index.cjs
CHANGED
|
@@ -7,35 +7,128 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
7
7
|
var MISSING_API_CLIENT_ERROR = "useApiClient must be used within <ApiClientProvider />";
|
|
8
8
|
var MISSING_PROJECT_ERROR = "useProject must be used within <ApiClientProvider />";
|
|
9
9
|
var MISSING_STACK_ERROR = "useStack must be used within <ApiClientProvider />";
|
|
10
|
-
var
|
|
11
|
-
var
|
|
10
|
+
var MISSING_AUTH_ERROR = "useAuth must be used within <AuthProvider />";
|
|
11
|
+
var MISSING_LEGACY_AUTH_ERROR = "useLegacyAuth must be used within <LegacyAuthProvider />";
|
|
12
|
+
var ApiContext = react.createContext(null);
|
|
13
|
+
var ApiProvider = ApiContext;
|
|
14
|
+
var AuthContext = react.createContext(null);
|
|
15
|
+
var AuthContextProvider = AuthContext;
|
|
16
|
+
var LegacyAuthContext = react.createContext(null);
|
|
17
|
+
var LegacyAuthContextProvider = LegacyAuthContext;
|
|
12
18
|
var useApiClient = () => {
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
15
|
-
return
|
|
19
|
+
const api = react.use(ApiContext);
|
|
20
|
+
if (api === null) throw new Error(MISSING_API_CLIENT_ERROR);
|
|
21
|
+
return api;
|
|
16
22
|
};
|
|
17
23
|
var useProject = () => {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
24
|
+
const api = react.use(ApiContext);
|
|
25
|
+
if (api === null) throw new Error(MISSING_PROJECT_ERROR);
|
|
26
|
+
return api.context.project;
|
|
21
27
|
};
|
|
22
28
|
var useStack = () => {
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
29
|
+
const api = react.use(ApiContext);
|
|
30
|
+
if (api === null) throw new Error(MISSING_STACK_ERROR);
|
|
31
|
+
return api.context.stack;
|
|
26
32
|
};
|
|
27
|
-
var
|
|
28
|
-
const
|
|
33
|
+
var useAuth = () => {
|
|
34
|
+
const session = react.use(AuthContext);
|
|
35
|
+
if (session === null) throw new Error(MISSING_AUTH_ERROR);
|
|
36
|
+
return session;
|
|
37
|
+
};
|
|
38
|
+
var useLegacyAuth = () => {
|
|
39
|
+
const session = react.use(LegacyAuthContext);
|
|
40
|
+
if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
|
|
41
|
+
return session;
|
|
42
|
+
};
|
|
43
|
+
var ApiClientProvider = ({ api, children }) => /* @__PURE__ */ jsxRuntime.jsx(ApiProvider, { value: api, children });
|
|
44
|
+
var AuthProvider = ({
|
|
45
|
+
session,
|
|
46
|
+
baseUrl,
|
|
47
|
+
credential,
|
|
48
|
+
projectId,
|
|
49
|
+
onAccessTokenChange,
|
|
50
|
+
loadingFallback = null,
|
|
51
|
+
errorFallback = null,
|
|
52
|
+
anonymousFallback = null,
|
|
53
|
+
children
|
|
54
|
+
}) => {
|
|
55
|
+
const [snapshot, setSnapshot] = react.useState(() => session.debug());
|
|
56
|
+
const [isRestoring, setIsRestoring] = react.useState(() => session.hasPersistedValue());
|
|
57
|
+
const [restoreStatus, setRestoreStatus] = react.useState(null);
|
|
58
|
+
const restoreStartedRef = react.useRef(false);
|
|
59
|
+
react.useEffect(() => session.subscribe(setSnapshot), [session]);
|
|
60
|
+
react.useEffect(() => {
|
|
61
|
+
if (baseUrl === void 0) return;
|
|
62
|
+
if (snapshot.config.baseUrl !== null) return;
|
|
63
|
+
session.setBaseUrl(baseUrl);
|
|
64
|
+
}, [session, baseUrl, snapshot.config.baseUrl]);
|
|
65
|
+
react.useEffect(() => {
|
|
66
|
+
if (restoreStartedRef.current) return;
|
|
67
|
+
restoreStartedRef.current = true;
|
|
68
|
+
session.restore().then((result) => setRestoreStatus(result.status)).catch((error) => {
|
|
69
|
+
console.error("Keboola bearer session restore failed", error);
|
|
70
|
+
setRestoreStatus("empty");
|
|
71
|
+
}).finally(() => setIsRestoring(false));
|
|
72
|
+
}, [session]);
|
|
73
|
+
react.useEffect(() => {
|
|
74
|
+
if (restoreStatus === null || restoreStatus === "restored") return;
|
|
75
|
+
if (credential === void 0) return;
|
|
76
|
+
if (snapshot.config.status !== "anonymous") return;
|
|
77
|
+
session.authenticate(credential);
|
|
78
|
+
}, [session, restoreStatus, credential, snapshot.config.status]);
|
|
79
|
+
react.useEffect(() => {
|
|
80
|
+
if (restoreStatus === null || restoreStatus === "restored") return;
|
|
81
|
+
if (projectId === void 0) return;
|
|
82
|
+
if (snapshot.config.status !== "authenticated") return;
|
|
83
|
+
session.setProject(projectId);
|
|
84
|
+
}, [session, restoreStatus, projectId, snapshot.config.status]);
|
|
85
|
+
const accessToken = snapshot.config.status === "anonymous" ? void 0 : snapshot.config.credential.accessToken;
|
|
86
|
+
react.useEffect(() => {
|
|
87
|
+
if (accessToken !== void 0) onAccessTokenChange?.(accessToken);
|
|
88
|
+
}, [accessToken, onAccessTokenChange]);
|
|
89
|
+
const isProgrammatic = credential !== void 0 && projectId !== void 0;
|
|
90
|
+
const content = (() => {
|
|
91
|
+
if (snapshot.storage === "ready") {
|
|
92
|
+
if (typeof children !== "function") return children;
|
|
93
|
+
if (snapshot.config.status !== "scoped") return null;
|
|
94
|
+
const { projectId: scopedProjectId, baseUrl: scopedBaseUrl } = snapshot.config;
|
|
95
|
+
return children({ projectId: scopedProjectId, baseUrl: scopedBaseUrl });
|
|
96
|
+
}
|
|
97
|
+
if (snapshot.storage === "errored") return errorFallback;
|
|
98
|
+
if (isRestoring || snapshot.storage === "pending" || isProgrammatic) return loadingFallback;
|
|
99
|
+
return anonymousFallback;
|
|
100
|
+
})();
|
|
101
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContextProvider, { value: session, children: content });
|
|
102
|
+
};
|
|
103
|
+
var LegacyAuthProvider = ({
|
|
104
|
+
session,
|
|
105
|
+
baseUrl,
|
|
106
|
+
storageToken,
|
|
107
|
+
managementToken,
|
|
108
|
+
fallback = null,
|
|
109
|
+
children
|
|
110
|
+
}) => {
|
|
111
|
+
const [isReady, setIsReady] = react.useState(() => session.isStorageReady());
|
|
112
|
+
const startedRef = react.useRef(false);
|
|
29
113
|
react.useEffect(() => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
114
|
+
const unsubscribe = session.subscribe(() => setIsReady(session.isStorageReady()));
|
|
115
|
+
if (!startedRef.current) {
|
|
116
|
+
startedRef.current = true;
|
|
117
|
+
session.setBaseUrl(baseUrl);
|
|
118
|
+
if (managementToken) session.authenticateManagement(managementToken);
|
|
119
|
+
void session.authenticateStorageAsync(storageToken).catch((error) => console.error("Keboola session bootstrap failed", error));
|
|
120
|
+
}
|
|
121
|
+
return unsubscribe;
|
|
33
122
|
}, []);
|
|
34
|
-
return
|
|
123
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LegacyAuthContextProvider, { value: session, children: isReady ? children : fallback });
|
|
35
124
|
};
|
|
36
125
|
|
|
37
126
|
exports.ApiClientProvider = ApiClientProvider;
|
|
127
|
+
exports.AuthProvider = AuthProvider;
|
|
128
|
+
exports.LegacyAuthProvider = LegacyAuthProvider;
|
|
38
129
|
exports.useApiClient = useApiClient;
|
|
130
|
+
exports.useAuth = useAuth;
|
|
131
|
+
exports.useLegacyAuth = useLegacyAuth;
|
|
39
132
|
exports.useProject = useProject;
|
|
40
133
|
exports.useStack = useStack;
|
|
41
134
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/ApiClientProvider.tsx"],"names":["createContext","use","useState","useEffect","jsx"],"mappings":";;;;;;AAIA,IAAM,wBAAA,GAA2B,wDAAA;AACjC,IAAM,qBAAA,GAAwB,sDAAA;AAC9B,IAAM,mBAAA,GAAsB,oDAAA;AAE5B,IAAM,OAAA,GAAUA,oBAAgC,IAAI,CAAA;AAE7C,IAAM,QAAA,GAAW,OAAA;AAEjB,IAAM,eAAe,MAAM;AAChC,EAAA,MAAM,MAAA,GAASC,UAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAC7D,EAAA,OAAO,MAAA;AACT;AAEO,IAAM,aAAa,MAAM;AAC9B,EAAA,MAAM,MAAA,GAASA,UAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,qBAAqB,CAAA;AAC1D,EAAA,OAAO,OAAO,OAAA,CAAQ,OAAA;AACxB;AAEO,IAAM,WAAW,MAAM;AAC5B,EAAA,MAAM,MAAA,GAASA,UAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,mBAAmB,CAAA;AACxD,EAAA,OAAO,OAAO,OAAA,CAAQ,KAAA;AACxB;ACfO,IAAM,iBAAA,GAAoB,CAAC,EAAE,MAAA,EAAQ,UAAU,QAAA,GAAW,IAAA,EAAM,GAAG,IAAA,EAAK,KAAa;AAC1F,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIC,eAAkB,IAAI,CAAA;AAElE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,OAAA,CAAQ,MAAM;AAC9B,MAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,IACzB,CAAC,CAAA;AAAA,EAGH,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,iBAAiB,QAAA,mBAAWC,cAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAO,QAAS,QAAA,EAAS,CAAA;AACxE","file":"index.cjs","sourcesContent":["import { createContext, use } from 'react';\n\nimport type { ApiClient } from '@keboola/api-client';\n\nconst MISSING_API_CLIENT_ERROR = 'useApiClient must be used within <ApiClientProvider />';\nconst MISSING_PROJECT_ERROR = 'useProject must be used within <ApiClientProvider />';\nconst MISSING_STACK_ERROR = 'useStack must be used within <ApiClientProvider />';\n\nconst Context = createContext<ApiClient | null>(null);\n\nexport const Provider = Context;\n\nexport const useApiClient = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_API_CLIENT_ERROR);\n return client;\n};\n\nexport const useProject = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_PROJECT_ERROR);\n return client.context.project;\n};\n\nexport const useStack = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_STACK_ERROR);\n return client.context.stack;\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { ApiClient, ApiClientOptions } from '@keboola/api-client';\n\nimport { Provider } from './context';\n\ntype Props = {\n client: ApiClient;\n children: ReactNode;\n fallback?: ReactNode;\n} & ApiClientOptions;\n\nexport const ApiClientProvider = ({ client, children, fallback = null, ...rest }: Props) => {\n const [isInitializing, setIsInitializing] = useState<boolean>(true);\n\n useEffect(() => {\n client.init(rest).finally(() => {\n setIsInitializing(false);\n });\n // we want to init only on initial mount\n // oxlint-disable-next-line react/exhaustive-deps\n }, []);\n\n return isInitializing ? fallback : <Provider value={client}>{children}</Provider>;\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/ApiClientProvider.tsx","../src/AuthProvider.tsx","../src/LegacyAuthProvider.tsx"],"names":["createContext","use","jsx","useState","useRef","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, use } from 'react';\n\nimport type { BearerSession, KeboolaApi, LegacySession } from '@keboola/api-client';\n\nconst MISSING_API_CLIENT_ERROR = 'useApiClient must be used within <ApiClientProvider />';\nconst MISSING_PROJECT_ERROR = 'useProject must be used within <ApiClientProvider />';\nconst MISSING_STACK_ERROR = 'useStack must be used within <ApiClientProvider />';\nconst MISSING_AUTH_ERROR = 'useAuth must be used within <AuthProvider />';\nconst MISSING_LEGACY_AUTH_ERROR = 'useLegacyAuth must be used within <LegacyAuthProvider />';\n\n// The product API (`auth` + `management` + every product namespace), flow-agnostic. Both the\n// bearer and legacy flows produce the same `KeboolaApi` surface, so consumers never branch on flow.\nconst ApiContext = createContext<KeboolaApi | null>(null);\nexport const ApiProvider = ApiContext;\n\n// The lifecycle sessions live in their own, flow-specific contexts so the auth surface a component\n// reaches for is explicit. Only the matching provider is mounted per flow.\nconst AuthContext = createContext<BearerSession | null>(null);\nexport const AuthContextProvider = AuthContext;\n\nconst LegacyAuthContext = createContext<LegacySession | null>(null);\nexport const LegacyAuthContextProvider = LegacyAuthContext;\n\nexport const useApiClient = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_API_CLIENT_ERROR);\n return api;\n};\n\nexport const useProject = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_PROJECT_ERROR);\n return api.context.project;\n};\n\nexport const useStack = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_STACK_ERROR);\n return api.context.stack;\n};\n\n/** The bearer (OAuth) lifecycle session. Throws outside `<AuthProvider />`. */\nexport const useAuth = () => {\n const session = use(AuthContext);\n if (session === null) throw new Error(MISSING_AUTH_ERROR);\n return session;\n};\n\n/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */\nexport const useLegacyAuth = () => {\n const session = use(LegacyAuthContext);\n if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);\n return session;\n};\n","import type { ReactNode } from 'react';\n\nimport type { KeboolaApi } from '@keboola/api-client';\n\nimport { ApiProvider } from './context';\n\ntype Props = {\n /** The flow-agnostic product API — the `api` half of a keboola client. */\n api: KeboolaApi;\n children: ReactNode;\n};\n\n/**\n * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving\n * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount\n * this provider once their session is ready.\n */\nexport const ApiClientProvider = ({ api, children }: Props) => (\n <ApiProvider value={api}>{children}</ApiProvider>\n);\n","import { useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\nimport { AuthContextProvider } from './context';\n\ntype BearerCredential = Parameters<BearerSession['authenticate']>[0];\ntype Snapshot = ReturnType<BearerSession['debug']>;\ntype RestoreStatus = Awaited<ReturnType<BearerSession['restore']>>['status'];\n\n/** The session state guaranteed to exist once the storage build is ready. */\ntype ScopedState = { projectId: number; baseUrl: string };\n\ntype BaseProps = {\n session: BearerSession;\n onAccessTokenChange?: (accessToken: string) => void;\n /** Rendered while a persisted session is being restored, or the bootstrap is in flight. */\n loadingFallback?: ReactNode;\n /** Rendered when the storage build fails. */\n errorFallback?: ReactNode;\n /**\n * Rendered only once the session is scoped and the storage build is ready. Pass a function to\n * receive the scoped state (the project the session settled on) without re-narrowing it yourself.\n */\n children: ReactNode | ((scoped: ScopedState) => ReactNode);\n};\n\n/**\n * Two mutually exclusive bootstrap modes, discriminated by whether a credential is supplied:\n * - **programmatic** — `credential` + `projectId` given, so `baseUrl` is **required**: a credential\n * is bound to the stack that issued it and `authenticate` refuses an unconfigured session.\n * - **interactive** — none of the three given; the stack comes from the session instead — `restore()`\n * adopts the persisted one, otherwise the login UI sets it. `anonymousFallback` is **required**\n * here (a missing login UI would leave the app on a permanently blank screen).\n */\ntype Props = BaseProps &\n (\n | {\n credential: BearerCredential;\n projectId: number;\n baseUrl: string;\n anonymousFallback?: never;\n }\n | {\n credential?: undefined;\n projectId?: undefined;\n baseUrl?: never;\n anonymousFallback: ReactNode;\n }\n );\n\nexport const AuthProvider = ({\n session,\n baseUrl,\n credential,\n projectId,\n onAccessTokenChange,\n loadingFallback = null,\n errorFallback = null,\n anonymousFallback = null,\n children,\n}: Props) => {\n const [snapshot, setSnapshot] = useState<Snapshot>(() => session.debug());\n const [isRestoring, setIsRestoring] = useState(() => session.hasPersistedValue());\n const [restoreStatus, setRestoreStatus] = useState<RestoreStatus | null>(null);\n const restoreStartedRef = useRef(false);\n\n useEffect(() => session.subscribe(setSnapshot), [session]);\n\n // Programmatic only. Interactive leaves the stack unset: `restore()` adopts the persisted one, and\n // failing that the login UI picks it — so there is nothing to configure up front.\n useEffect(() => {\n if (baseUrl === undefined) return;\n if (snapshot.config.baseUrl !== null) return;\n session.setBaseUrl(baseUrl);\n }, [session, baseUrl, snapshot.config.baseUrl]);\n\n // Runs regardless of whether a stack is configured — with none, `restore()` adopts the stack the\n // persisted session belongs to. (Declared after the effect above, so programmatic mode has already\n // configured its stack by the time this runs.) The ref guards StrictMode's double-invoked effect:\n // `restoreStatus` only lands asynchronously, so it cannot prevent a second synchronous call.\n useEffect(() => {\n if (restoreStartedRef.current) return;\n restoreStartedRef.current = true;\n session\n .restore()\n .then((result) => setRestoreStatus(result.status))\n .catch((error) => {\n console.error('Keboola bearer session restore failed', error);\n setRestoreStatus('empty');\n })\n .finally(() => setIsRestoring(false));\n }, [session]);\n\n useEffect(() => {\n if (restoreStatus === null || restoreStatus === 'restored') return;\n if (credential === undefined) return;\n if (snapshot.config.status !== 'anonymous') return;\n session.authenticate(credential);\n }, [session, restoreStatus, credential, snapshot.config.status]);\n\n useEffect(() => {\n if (restoreStatus === null || restoreStatus === 'restored') return;\n if (projectId === undefined) return;\n if (snapshot.config.status !== 'authenticated') return;\n session.setProject(projectId);\n }, [session, restoreStatus, projectId, snapshot.config.status]);\n\n const accessToken =\n snapshot.config.status === 'anonymous' ? undefined : snapshot.config.credential.accessToken;\n\n useEffect(() => {\n if (accessToken !== undefined) onAccessTokenChange?.(accessToken);\n }, [accessToken, onAccessTokenChange]);\n\n // Programmatic mode drives itself to ready, so it must never fall through to `anonymousFallback`.\n const isProgrammatic = credential !== undefined && projectId !== undefined;\n\n const content = (() => {\n if (snapshot.storage === 'ready') {\n if (typeof children !== 'function') return children;\n // A ready storage build implies a scoped session; narrowing here is for the type checker.\n if (snapshot.config.status !== 'scoped') return null;\n const { projectId: scopedProjectId, baseUrl: scopedBaseUrl } = snapshot.config;\n return children({ projectId: scopedProjectId, baseUrl: scopedBaseUrl });\n }\n if (snapshot.storage === 'errored') return errorFallback;\n if (isRestoring || snapshot.storage === 'pending' || isProgrammatic) return loadingFallback;\n return anonymousFallback;\n })();\n\n return <AuthContextProvider value={session}>{content}</AuthContextProvider>;\n};\n","import { useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { LegacySession } from '@keboola/api-client';\n\nimport { LegacyAuthContextProvider } from './context';\n\ntype Props = {\n session: LegacySession;\n /** Stack base URL. */\n baseUrl: string;\n /** SAPI token — drives the storage build to ready. */\n storageToken: string;\n /** Optional manage-api token — unlocks `api.management`. */\n managementToken?: string;\n /** Rendered until the storage build reaches ready. */\n fallback?: ReactNode;\n children: ReactNode;\n};\n\n/**\n * Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to\n * ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is\n * known up front, so this drives itself (unlike the interactive bearer `AuthProvider`). Exposes the\n * session via `useLegacyAuth()` and gates `children` behind readiness.\n */\nexport const LegacyAuthProvider = ({\n session,\n baseUrl,\n storageToken,\n managementToken,\n fallback = null,\n children,\n}: Props) => {\n const [isReady, setIsReady] = useState<boolean>(() => session.isStorageReady());\n // Guards the once-only drive against React StrictMode's double-mounted effect.\n const startedRef = useRef<boolean>(false);\n\n useEffect(() => {\n const unsubscribe = session.subscribe(() => setIsReady(session.isStorageReady()));\n\n if (!startedRef.current) {\n startedRef.current = true;\n session.setBaseUrl(baseUrl);\n if (managementToken) session.authenticateManagement(managementToken);\n // Fire-and-forget: `subscribe` flips `isReady` once the build resolves. A failure leaves the\n // fallback in place; log it so the bootstrap error isn't silent.\n void session\n .authenticateStorageAsync(storageToken)\n .catch((error) => console.error('Keboola session bootstrap failed', error));\n }\n\n return unsubscribe;\n // drive to ready only on initial mount\n // oxlint-disable-next-line react/exhaustive-deps\n }, []);\n\n return (\n <LegacyAuthContextProvider value={session}>\n {isReady ? children : fallback}\n </LegacyAuthContextProvider>\n );\n};\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as _keboola_api_client_storage_types from '@keboola/api-client/storage/types';
|
|
2
|
-
import
|
|
2
|
+
import * as _keboola_api_client from '@keboola/api-client';
|
|
3
|
+
import { KeboolaApi, BearerSession, LegacySession } from '@keboola/api-client';
|
|
3
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
-
import * as react from 'react';
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
|
|
7
|
-
declare const useApiClient: () =>
|
|
7
|
+
declare const useApiClient: () => KeboolaApi;
|
|
8
8
|
declare const useProject: () => {
|
|
9
9
|
hasFeature: (...feature: string[]) => boolean;
|
|
10
10
|
hasAdminFeature: (...feature: string[]) => boolean;
|
|
@@ -30,12 +30,187 @@ declare const useStack: () => {
|
|
|
30
30
|
};
|
|
31
31
|
version: string;
|
|
32
32
|
};
|
|
33
|
+
/** The bearer (OAuth) lifecycle session. Throws outside `<AuthProvider />`. */
|
|
34
|
+
declare const useAuth: () => {
|
|
35
|
+
hasPersistedValue: () => boolean;
|
|
36
|
+
setBaseUrl: (baseUrl: string) => void;
|
|
37
|
+
isStorageReady: () => boolean;
|
|
38
|
+
authenticate: (tokens: {
|
|
39
|
+
accessToken: string;
|
|
40
|
+
refreshToken: string;
|
|
41
|
+
expiresIn: number;
|
|
42
|
+
}) => void;
|
|
43
|
+
setProject: (projectId: number) => void;
|
|
44
|
+
setProjectAsync: (projectId: number) => Promise<void>;
|
|
45
|
+
reset: () => void;
|
|
46
|
+
debugForceRefresh: () => void;
|
|
47
|
+
restore: () => Promise<{
|
|
48
|
+
status: "empty" | "restored" | "expired";
|
|
49
|
+
}>;
|
|
50
|
+
subscribe: (listener: (snapshot: {
|
|
51
|
+
type: "Bearer";
|
|
52
|
+
config: {
|
|
53
|
+
status: "anonymous";
|
|
54
|
+
baseUrl: string | null;
|
|
55
|
+
} | {
|
|
56
|
+
status: "authenticated";
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
credential: {
|
|
59
|
+
accessToken: string;
|
|
60
|
+
refreshToken: string;
|
|
61
|
+
expiresIn: number;
|
|
62
|
+
} & {
|
|
63
|
+
issuedAt: number;
|
|
64
|
+
};
|
|
65
|
+
} | {
|
|
66
|
+
status: "scoped";
|
|
67
|
+
baseUrl: string;
|
|
68
|
+
credential: {
|
|
69
|
+
accessToken: string;
|
|
70
|
+
refreshToken: string;
|
|
71
|
+
expiresIn: number;
|
|
72
|
+
} & {
|
|
73
|
+
issuedAt: number;
|
|
74
|
+
};
|
|
75
|
+
projectId: number;
|
|
76
|
+
};
|
|
77
|
+
authReady: boolean;
|
|
78
|
+
managementReady: boolean;
|
|
79
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
80
|
+
}) => void) => () => void;
|
|
81
|
+
debug: () => {
|
|
82
|
+
type: "Bearer";
|
|
83
|
+
config: {
|
|
84
|
+
status: "anonymous";
|
|
85
|
+
baseUrl: string | null;
|
|
86
|
+
} | {
|
|
87
|
+
status: "authenticated";
|
|
88
|
+
baseUrl: string;
|
|
89
|
+
credential: {
|
|
90
|
+
accessToken: string;
|
|
91
|
+
refreshToken: string;
|
|
92
|
+
expiresIn: number;
|
|
93
|
+
} & {
|
|
94
|
+
issuedAt: number;
|
|
95
|
+
};
|
|
96
|
+
} | {
|
|
97
|
+
status: "scoped";
|
|
98
|
+
baseUrl: string;
|
|
99
|
+
credential: {
|
|
100
|
+
accessToken: string;
|
|
101
|
+
refreshToken: string;
|
|
102
|
+
expiresIn: number;
|
|
103
|
+
} & {
|
|
104
|
+
issuedAt: number;
|
|
105
|
+
};
|
|
106
|
+
projectId: number;
|
|
107
|
+
};
|
|
108
|
+
authReady: boolean;
|
|
109
|
+
managementReady: boolean;
|
|
110
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
114
|
+
declare const useLegacyAuth: () => {
|
|
115
|
+
setBaseUrl: (baseUrl: string) => void;
|
|
116
|
+
isStorageReady: () => boolean;
|
|
117
|
+
authenticateStorage: (sapiToken: string) => void;
|
|
118
|
+
authenticateStorageAsync: (sapiToken: string) => Promise<void>;
|
|
119
|
+
authenticateManagement: (managementToken: string) => void;
|
|
120
|
+
subscribe: (listener: (snapshot: {
|
|
121
|
+
type: "Sapi";
|
|
122
|
+
config: {
|
|
123
|
+
baseUrl: string | null;
|
|
124
|
+
sapiToken: string | null;
|
|
125
|
+
managementToken: string | null;
|
|
126
|
+
};
|
|
127
|
+
authReady: boolean;
|
|
128
|
+
managementReady: boolean;
|
|
129
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
130
|
+
}) => void) => () => void;
|
|
131
|
+
debug: () => {
|
|
132
|
+
type: "Sapi";
|
|
133
|
+
config: {
|
|
134
|
+
baseUrl: string | null;
|
|
135
|
+
sapiToken: string | null;
|
|
136
|
+
managementToken: string | null;
|
|
137
|
+
};
|
|
138
|
+
authReady: boolean;
|
|
139
|
+
managementReady: boolean;
|
|
140
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
141
|
+
};
|
|
142
|
+
};
|
|
33
143
|
|
|
34
|
-
type Props = {
|
|
35
|
-
client
|
|
144
|
+
type Props$2 = {
|
|
145
|
+
/** The flow-agnostic product API — the `api` half of a keboola client. */
|
|
146
|
+
api: KeboolaApi;
|
|
36
147
|
children: ReactNode;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
|
|
151
|
+
* to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
|
|
152
|
+
* this provider once their session is ready.
|
|
153
|
+
*/
|
|
154
|
+
declare const ApiClientProvider: ({ api, children }: Props$2) => react_jsx_runtime.JSX.Element;
|
|
155
|
+
|
|
156
|
+
type BearerCredential = Parameters<BearerSession['authenticate']>[0];
|
|
157
|
+
/** The session state guaranteed to exist once the storage build is ready. */
|
|
158
|
+
type ScopedState = {
|
|
159
|
+
projectId: number;
|
|
160
|
+
baseUrl: string;
|
|
161
|
+
};
|
|
162
|
+
type BaseProps = {
|
|
163
|
+
session: BearerSession;
|
|
164
|
+
onAccessTokenChange?: (accessToken: string) => void;
|
|
165
|
+
/** Rendered while a persisted session is being restored, or the bootstrap is in flight. */
|
|
166
|
+
loadingFallback?: ReactNode;
|
|
167
|
+
/** Rendered when the storage build fails. */
|
|
168
|
+
errorFallback?: ReactNode;
|
|
169
|
+
/**
|
|
170
|
+
* Rendered only once the session is scoped and the storage build is ready. Pass a function to
|
|
171
|
+
* receive the scoped state (the project the session settled on) without re-narrowing it yourself.
|
|
172
|
+
*/
|
|
173
|
+
children: ReactNode | ((scoped: ScopedState) => ReactNode);
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Two mutually exclusive bootstrap modes, discriminated by whether a credential is supplied:
|
|
177
|
+
* - **programmatic** — `credential` + `projectId` given, so `baseUrl` is **required**: a credential
|
|
178
|
+
* is bound to the stack that issued it and `authenticate` refuses an unconfigured session.
|
|
179
|
+
* - **interactive** — none of the three given; the stack comes from the session instead — `restore()`
|
|
180
|
+
* adopts the persisted one, otherwise the login UI sets it. `anonymousFallback` is **required**
|
|
181
|
+
* here (a missing login UI would leave the app on a permanently blank screen).
|
|
182
|
+
*/
|
|
183
|
+
type Props$1 = BaseProps & ({
|
|
184
|
+
credential: BearerCredential;
|
|
185
|
+
projectId: number;
|
|
186
|
+
baseUrl: string;
|
|
187
|
+
anonymousFallback?: never;
|
|
188
|
+
} | {
|
|
189
|
+
credential?: undefined;
|
|
190
|
+
projectId?: undefined;
|
|
191
|
+
baseUrl?: never;
|
|
192
|
+
anonymousFallback: ReactNode;
|
|
193
|
+
});
|
|
194
|
+
declare const AuthProvider: ({ session, baseUrl, credential, projectId, onAccessTokenChange, loadingFallback, errorFallback, anonymousFallback, children, }: Props$1) => react_jsx_runtime.JSX.Element;
|
|
195
|
+
|
|
196
|
+
type Props = {
|
|
197
|
+
session: LegacySession;
|
|
198
|
+
/** Stack base URL. */
|
|
199
|
+
baseUrl: string;
|
|
200
|
+
/** SAPI token — drives the storage build to ready. */
|
|
201
|
+
storageToken: string;
|
|
202
|
+
/** Optional manage-api token — unlocks `api.management`. */
|
|
203
|
+
managementToken?: string;
|
|
204
|
+
/** Rendered until the storage build reaches ready. */
|
|
37
205
|
fallback?: ReactNode;
|
|
38
|
-
|
|
39
|
-
|
|
206
|
+
children: ReactNode;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
|
|
210
|
+
* ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
|
|
211
|
+
* known up front, so this drives itself (unlike the interactive bearer `AuthProvider`). Exposes the
|
|
212
|
+
* session via `useLegacyAuth()` and gates `children` behind readiness.
|
|
213
|
+
*/
|
|
214
|
+
declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, children, }: Props) => react_jsx_runtime.JSX.Element;
|
|
40
215
|
|
|
41
|
-
export { ApiClientProvider, useApiClient, useProject, useStack };
|
|
216
|
+
export { ApiClientProvider, AuthProvider, LegacyAuthProvider, useApiClient, useAuth, useLegacyAuth, useProject, useStack };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as _keboola_api_client_storage_types from '@keboola/api-client/storage/types';
|
|
2
|
-
import
|
|
2
|
+
import * as _keboola_api_client from '@keboola/api-client';
|
|
3
|
+
import { KeboolaApi, BearerSession, LegacySession } from '@keboola/api-client';
|
|
3
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
-
import * as react from 'react';
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
|
|
7
|
-
declare const useApiClient: () =>
|
|
7
|
+
declare const useApiClient: () => KeboolaApi;
|
|
8
8
|
declare const useProject: () => {
|
|
9
9
|
hasFeature: (...feature: string[]) => boolean;
|
|
10
10
|
hasAdminFeature: (...feature: string[]) => boolean;
|
|
@@ -30,12 +30,187 @@ declare const useStack: () => {
|
|
|
30
30
|
};
|
|
31
31
|
version: string;
|
|
32
32
|
};
|
|
33
|
+
/** The bearer (OAuth) lifecycle session. Throws outside `<AuthProvider />`. */
|
|
34
|
+
declare const useAuth: () => {
|
|
35
|
+
hasPersistedValue: () => boolean;
|
|
36
|
+
setBaseUrl: (baseUrl: string) => void;
|
|
37
|
+
isStorageReady: () => boolean;
|
|
38
|
+
authenticate: (tokens: {
|
|
39
|
+
accessToken: string;
|
|
40
|
+
refreshToken: string;
|
|
41
|
+
expiresIn: number;
|
|
42
|
+
}) => void;
|
|
43
|
+
setProject: (projectId: number) => void;
|
|
44
|
+
setProjectAsync: (projectId: number) => Promise<void>;
|
|
45
|
+
reset: () => void;
|
|
46
|
+
debugForceRefresh: () => void;
|
|
47
|
+
restore: () => Promise<{
|
|
48
|
+
status: "empty" | "restored" | "expired";
|
|
49
|
+
}>;
|
|
50
|
+
subscribe: (listener: (snapshot: {
|
|
51
|
+
type: "Bearer";
|
|
52
|
+
config: {
|
|
53
|
+
status: "anonymous";
|
|
54
|
+
baseUrl: string | null;
|
|
55
|
+
} | {
|
|
56
|
+
status: "authenticated";
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
credential: {
|
|
59
|
+
accessToken: string;
|
|
60
|
+
refreshToken: string;
|
|
61
|
+
expiresIn: number;
|
|
62
|
+
} & {
|
|
63
|
+
issuedAt: number;
|
|
64
|
+
};
|
|
65
|
+
} | {
|
|
66
|
+
status: "scoped";
|
|
67
|
+
baseUrl: string;
|
|
68
|
+
credential: {
|
|
69
|
+
accessToken: string;
|
|
70
|
+
refreshToken: string;
|
|
71
|
+
expiresIn: number;
|
|
72
|
+
} & {
|
|
73
|
+
issuedAt: number;
|
|
74
|
+
};
|
|
75
|
+
projectId: number;
|
|
76
|
+
};
|
|
77
|
+
authReady: boolean;
|
|
78
|
+
managementReady: boolean;
|
|
79
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
80
|
+
}) => void) => () => void;
|
|
81
|
+
debug: () => {
|
|
82
|
+
type: "Bearer";
|
|
83
|
+
config: {
|
|
84
|
+
status: "anonymous";
|
|
85
|
+
baseUrl: string | null;
|
|
86
|
+
} | {
|
|
87
|
+
status: "authenticated";
|
|
88
|
+
baseUrl: string;
|
|
89
|
+
credential: {
|
|
90
|
+
accessToken: string;
|
|
91
|
+
refreshToken: string;
|
|
92
|
+
expiresIn: number;
|
|
93
|
+
} & {
|
|
94
|
+
issuedAt: number;
|
|
95
|
+
};
|
|
96
|
+
} | {
|
|
97
|
+
status: "scoped";
|
|
98
|
+
baseUrl: string;
|
|
99
|
+
credential: {
|
|
100
|
+
accessToken: string;
|
|
101
|
+
refreshToken: string;
|
|
102
|
+
expiresIn: number;
|
|
103
|
+
} & {
|
|
104
|
+
issuedAt: number;
|
|
105
|
+
};
|
|
106
|
+
projectId: number;
|
|
107
|
+
};
|
|
108
|
+
authReady: boolean;
|
|
109
|
+
managementReady: boolean;
|
|
110
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
114
|
+
declare const useLegacyAuth: () => {
|
|
115
|
+
setBaseUrl: (baseUrl: string) => void;
|
|
116
|
+
isStorageReady: () => boolean;
|
|
117
|
+
authenticateStorage: (sapiToken: string) => void;
|
|
118
|
+
authenticateStorageAsync: (sapiToken: string) => Promise<void>;
|
|
119
|
+
authenticateManagement: (managementToken: string) => void;
|
|
120
|
+
subscribe: (listener: (snapshot: {
|
|
121
|
+
type: "Sapi";
|
|
122
|
+
config: {
|
|
123
|
+
baseUrl: string | null;
|
|
124
|
+
sapiToken: string | null;
|
|
125
|
+
managementToken: string | null;
|
|
126
|
+
};
|
|
127
|
+
authReady: boolean;
|
|
128
|
+
managementReady: boolean;
|
|
129
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
130
|
+
}) => void) => () => void;
|
|
131
|
+
debug: () => {
|
|
132
|
+
type: "Sapi";
|
|
133
|
+
config: {
|
|
134
|
+
baseUrl: string | null;
|
|
135
|
+
sapiToken: string | null;
|
|
136
|
+
managementToken: string | null;
|
|
137
|
+
};
|
|
138
|
+
authReady: boolean;
|
|
139
|
+
managementReady: boolean;
|
|
140
|
+
storage: _keboola_api_client.AsyncState<unknown>["state"];
|
|
141
|
+
};
|
|
142
|
+
};
|
|
33
143
|
|
|
34
|
-
type Props = {
|
|
35
|
-
client
|
|
144
|
+
type Props$2 = {
|
|
145
|
+
/** The flow-agnostic product API — the `api` half of a keboola client. */
|
|
146
|
+
api: KeboolaApi;
|
|
36
147
|
children: ReactNode;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
|
|
151
|
+
* to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
|
|
152
|
+
* this provider once their session is ready.
|
|
153
|
+
*/
|
|
154
|
+
declare const ApiClientProvider: ({ api, children }: Props$2) => react_jsx_runtime.JSX.Element;
|
|
155
|
+
|
|
156
|
+
type BearerCredential = Parameters<BearerSession['authenticate']>[0];
|
|
157
|
+
/** The session state guaranteed to exist once the storage build is ready. */
|
|
158
|
+
type ScopedState = {
|
|
159
|
+
projectId: number;
|
|
160
|
+
baseUrl: string;
|
|
161
|
+
};
|
|
162
|
+
type BaseProps = {
|
|
163
|
+
session: BearerSession;
|
|
164
|
+
onAccessTokenChange?: (accessToken: string) => void;
|
|
165
|
+
/** Rendered while a persisted session is being restored, or the bootstrap is in flight. */
|
|
166
|
+
loadingFallback?: ReactNode;
|
|
167
|
+
/** Rendered when the storage build fails. */
|
|
168
|
+
errorFallback?: ReactNode;
|
|
169
|
+
/**
|
|
170
|
+
* Rendered only once the session is scoped and the storage build is ready. Pass a function to
|
|
171
|
+
* receive the scoped state (the project the session settled on) without re-narrowing it yourself.
|
|
172
|
+
*/
|
|
173
|
+
children: ReactNode | ((scoped: ScopedState) => ReactNode);
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Two mutually exclusive bootstrap modes, discriminated by whether a credential is supplied:
|
|
177
|
+
* - **programmatic** — `credential` + `projectId` given, so `baseUrl` is **required**: a credential
|
|
178
|
+
* is bound to the stack that issued it and `authenticate` refuses an unconfigured session.
|
|
179
|
+
* - **interactive** — none of the three given; the stack comes from the session instead — `restore()`
|
|
180
|
+
* adopts the persisted one, otherwise the login UI sets it. `anonymousFallback` is **required**
|
|
181
|
+
* here (a missing login UI would leave the app on a permanently blank screen).
|
|
182
|
+
*/
|
|
183
|
+
type Props$1 = BaseProps & ({
|
|
184
|
+
credential: BearerCredential;
|
|
185
|
+
projectId: number;
|
|
186
|
+
baseUrl: string;
|
|
187
|
+
anonymousFallback?: never;
|
|
188
|
+
} | {
|
|
189
|
+
credential?: undefined;
|
|
190
|
+
projectId?: undefined;
|
|
191
|
+
baseUrl?: never;
|
|
192
|
+
anonymousFallback: ReactNode;
|
|
193
|
+
});
|
|
194
|
+
declare const AuthProvider: ({ session, baseUrl, credential, projectId, onAccessTokenChange, loadingFallback, errorFallback, anonymousFallback, children, }: Props$1) => react_jsx_runtime.JSX.Element;
|
|
195
|
+
|
|
196
|
+
type Props = {
|
|
197
|
+
session: LegacySession;
|
|
198
|
+
/** Stack base URL. */
|
|
199
|
+
baseUrl: string;
|
|
200
|
+
/** SAPI token — drives the storage build to ready. */
|
|
201
|
+
storageToken: string;
|
|
202
|
+
/** Optional manage-api token — unlocks `api.management`. */
|
|
203
|
+
managementToken?: string;
|
|
204
|
+
/** Rendered until the storage build reaches ready. */
|
|
37
205
|
fallback?: ReactNode;
|
|
38
|
-
|
|
39
|
-
|
|
206
|
+
children: ReactNode;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
|
|
210
|
+
* ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
|
|
211
|
+
* known up front, so this drives itself (unlike the interactive bearer `AuthProvider`). Exposes the
|
|
212
|
+
* session via `useLegacyAuth()` and gates `children` behind readiness.
|
|
213
|
+
*/
|
|
214
|
+
declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, children, }: Props) => react_jsx_runtime.JSX.Element;
|
|
40
215
|
|
|
41
|
-
export { ApiClientProvider, useApiClient, useProject, useStack };
|
|
216
|
+
export { ApiClientProvider, AuthProvider, LegacyAuthProvider, useApiClient, useAuth, useLegacyAuth, useProject, useStack };
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,126 @@
|
|
|
1
|
-
import { createContext, use, useState, useEffect } from 'react';
|
|
1
|
+
import { createContext, use, useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/context.ts
|
|
5
5
|
var MISSING_API_CLIENT_ERROR = "useApiClient must be used within <ApiClientProvider />";
|
|
6
6
|
var MISSING_PROJECT_ERROR = "useProject must be used within <ApiClientProvider />";
|
|
7
7
|
var MISSING_STACK_ERROR = "useStack must be used within <ApiClientProvider />";
|
|
8
|
-
var
|
|
9
|
-
var
|
|
8
|
+
var MISSING_AUTH_ERROR = "useAuth must be used within <AuthProvider />";
|
|
9
|
+
var MISSING_LEGACY_AUTH_ERROR = "useLegacyAuth must be used within <LegacyAuthProvider />";
|
|
10
|
+
var ApiContext = createContext(null);
|
|
11
|
+
var ApiProvider = ApiContext;
|
|
12
|
+
var AuthContext = createContext(null);
|
|
13
|
+
var AuthContextProvider = AuthContext;
|
|
14
|
+
var LegacyAuthContext = createContext(null);
|
|
15
|
+
var LegacyAuthContextProvider = LegacyAuthContext;
|
|
10
16
|
var useApiClient = () => {
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
13
|
-
return
|
|
17
|
+
const api = use(ApiContext);
|
|
18
|
+
if (api === null) throw new Error(MISSING_API_CLIENT_ERROR);
|
|
19
|
+
return api;
|
|
14
20
|
};
|
|
15
21
|
var useProject = () => {
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
return
|
|
22
|
+
const api = use(ApiContext);
|
|
23
|
+
if (api === null) throw new Error(MISSING_PROJECT_ERROR);
|
|
24
|
+
return api.context.project;
|
|
19
25
|
};
|
|
20
26
|
var useStack = () => {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
return
|
|
27
|
+
const api = use(ApiContext);
|
|
28
|
+
if (api === null) throw new Error(MISSING_STACK_ERROR);
|
|
29
|
+
return api.context.stack;
|
|
24
30
|
};
|
|
25
|
-
var
|
|
26
|
-
const
|
|
31
|
+
var useAuth = () => {
|
|
32
|
+
const session = use(AuthContext);
|
|
33
|
+
if (session === null) throw new Error(MISSING_AUTH_ERROR);
|
|
34
|
+
return session;
|
|
35
|
+
};
|
|
36
|
+
var useLegacyAuth = () => {
|
|
37
|
+
const session = use(LegacyAuthContext);
|
|
38
|
+
if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
|
|
39
|
+
return session;
|
|
40
|
+
};
|
|
41
|
+
var ApiClientProvider = ({ api, children }) => /* @__PURE__ */ jsx(ApiProvider, { value: api, children });
|
|
42
|
+
var AuthProvider = ({
|
|
43
|
+
session,
|
|
44
|
+
baseUrl,
|
|
45
|
+
credential,
|
|
46
|
+
projectId,
|
|
47
|
+
onAccessTokenChange,
|
|
48
|
+
loadingFallback = null,
|
|
49
|
+
errorFallback = null,
|
|
50
|
+
anonymousFallback = null,
|
|
51
|
+
children
|
|
52
|
+
}) => {
|
|
53
|
+
const [snapshot, setSnapshot] = useState(() => session.debug());
|
|
54
|
+
const [isRestoring, setIsRestoring] = useState(() => session.hasPersistedValue());
|
|
55
|
+
const [restoreStatus, setRestoreStatus] = useState(null);
|
|
56
|
+
const restoreStartedRef = useRef(false);
|
|
57
|
+
useEffect(() => session.subscribe(setSnapshot), [session]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (baseUrl === void 0) return;
|
|
60
|
+
if (snapshot.config.baseUrl !== null) return;
|
|
61
|
+
session.setBaseUrl(baseUrl);
|
|
62
|
+
}, [session, baseUrl, snapshot.config.baseUrl]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (restoreStartedRef.current) return;
|
|
65
|
+
restoreStartedRef.current = true;
|
|
66
|
+
session.restore().then((result) => setRestoreStatus(result.status)).catch((error) => {
|
|
67
|
+
console.error("Keboola bearer session restore failed", error);
|
|
68
|
+
setRestoreStatus("empty");
|
|
69
|
+
}).finally(() => setIsRestoring(false));
|
|
70
|
+
}, [session]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (restoreStatus === null || restoreStatus === "restored") return;
|
|
73
|
+
if (credential === void 0) return;
|
|
74
|
+
if (snapshot.config.status !== "anonymous") return;
|
|
75
|
+
session.authenticate(credential);
|
|
76
|
+
}, [session, restoreStatus, credential, snapshot.config.status]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (restoreStatus === null || restoreStatus === "restored") return;
|
|
79
|
+
if (projectId === void 0) return;
|
|
80
|
+
if (snapshot.config.status !== "authenticated") return;
|
|
81
|
+
session.setProject(projectId);
|
|
82
|
+
}, [session, restoreStatus, projectId, snapshot.config.status]);
|
|
83
|
+
const accessToken = snapshot.config.status === "anonymous" ? void 0 : snapshot.config.credential.accessToken;
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (accessToken !== void 0) onAccessTokenChange?.(accessToken);
|
|
86
|
+
}, [accessToken, onAccessTokenChange]);
|
|
87
|
+
const isProgrammatic = credential !== void 0 && projectId !== void 0;
|
|
88
|
+
const content = (() => {
|
|
89
|
+
if (snapshot.storage === "ready") {
|
|
90
|
+
if (typeof children !== "function") return children;
|
|
91
|
+
if (snapshot.config.status !== "scoped") return null;
|
|
92
|
+
const { projectId: scopedProjectId, baseUrl: scopedBaseUrl } = snapshot.config;
|
|
93
|
+
return children({ projectId: scopedProjectId, baseUrl: scopedBaseUrl });
|
|
94
|
+
}
|
|
95
|
+
if (snapshot.storage === "errored") return errorFallback;
|
|
96
|
+
if (isRestoring || snapshot.storage === "pending" || isProgrammatic) return loadingFallback;
|
|
97
|
+
return anonymousFallback;
|
|
98
|
+
})();
|
|
99
|
+
return /* @__PURE__ */ jsx(AuthContextProvider, { value: session, children: content });
|
|
100
|
+
};
|
|
101
|
+
var LegacyAuthProvider = ({
|
|
102
|
+
session,
|
|
103
|
+
baseUrl,
|
|
104
|
+
storageToken,
|
|
105
|
+
managementToken,
|
|
106
|
+
fallback = null,
|
|
107
|
+
children
|
|
108
|
+
}) => {
|
|
109
|
+
const [isReady, setIsReady] = useState(() => session.isStorageReady());
|
|
110
|
+
const startedRef = useRef(false);
|
|
27
111
|
useEffect(() => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
112
|
+
const unsubscribe = session.subscribe(() => setIsReady(session.isStorageReady()));
|
|
113
|
+
if (!startedRef.current) {
|
|
114
|
+
startedRef.current = true;
|
|
115
|
+
session.setBaseUrl(baseUrl);
|
|
116
|
+
if (managementToken) session.authenticateManagement(managementToken);
|
|
117
|
+
void session.authenticateStorageAsync(storageToken).catch((error) => console.error("Keboola session bootstrap failed", error));
|
|
118
|
+
}
|
|
119
|
+
return unsubscribe;
|
|
31
120
|
}, []);
|
|
32
|
-
return
|
|
121
|
+
return /* @__PURE__ */ jsx(LegacyAuthContextProvider, { value: session, children: isReady ? children : fallback });
|
|
33
122
|
};
|
|
34
123
|
|
|
35
|
-
export { ApiClientProvider, useApiClient, useProject, useStack };
|
|
124
|
+
export { ApiClientProvider, AuthProvider, LegacyAuthProvider, useApiClient, useAuth, useLegacyAuth, useProject, useStack };
|
|
36
125
|
//# sourceMappingURL=index.js.map
|
|
37
126
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/ApiClientProvider.tsx"],"names":[],"mappings":";;;;AAIA,IAAM,wBAAA,GAA2B,wDAAA;AACjC,IAAM,qBAAA,GAAwB,sDAAA;AAC9B,IAAM,mBAAA,GAAsB,oDAAA;AAE5B,IAAM,OAAA,GAAU,cAAgC,IAAI,CAAA;AAE7C,IAAM,QAAA,GAAW,OAAA;AAEjB,IAAM,eAAe,MAAM;AAChC,EAAA,MAAM,MAAA,GAAS,IAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAC7D,EAAA,OAAO,MAAA;AACT;AAEO,IAAM,aAAa,MAAM;AAC9B,EAAA,MAAM,MAAA,GAAS,IAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,qBAAqB,CAAA;AAC1D,EAAA,OAAO,OAAO,OAAA,CAAQ,OAAA;AACxB;AAEO,IAAM,WAAW,MAAM;AAC5B,EAAA,MAAM,MAAA,GAAS,IAAI,OAAO,CAAA;AAC1B,EAAA,IAAI,MAAA,KAAW,IAAA,EAAM,MAAM,IAAI,MAAM,mBAAmB,CAAA;AACxD,EAAA,OAAO,OAAO,OAAA,CAAQ,KAAA;AACxB;ACfO,IAAM,iBAAA,GAAoB,CAAC,EAAE,MAAA,EAAQ,UAAU,QAAA,GAAW,IAAA,EAAM,GAAG,IAAA,EAAK,KAAa;AAC1F,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAkB,IAAI,CAAA;AAElE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,OAAA,CAAQ,MAAM;AAC9B,MAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,IACzB,CAAC,CAAA;AAAA,EAGH,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,iBAAiB,QAAA,mBAAW,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAO,QAAS,QAAA,EAAS,CAAA;AACxE","file":"index.js","sourcesContent":["import { createContext, use } from 'react';\n\nimport type { ApiClient } from '@keboola/api-client';\n\nconst MISSING_API_CLIENT_ERROR = 'useApiClient must be used within <ApiClientProvider />';\nconst MISSING_PROJECT_ERROR = 'useProject must be used within <ApiClientProvider />';\nconst MISSING_STACK_ERROR = 'useStack must be used within <ApiClientProvider />';\n\nconst Context = createContext<ApiClient | null>(null);\n\nexport const Provider = Context;\n\nexport const useApiClient = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_API_CLIENT_ERROR);\n return client;\n};\n\nexport const useProject = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_PROJECT_ERROR);\n return client.context.project;\n};\n\nexport const useStack = () => {\n const client = use(Context);\n if (client === null) throw new Error(MISSING_STACK_ERROR);\n return client.context.stack;\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { ApiClient, ApiClientOptions } from '@keboola/api-client';\n\nimport { Provider } from './context';\n\ntype Props = {\n client: ApiClient;\n children: ReactNode;\n fallback?: ReactNode;\n} & ApiClientOptions;\n\nexport const ApiClientProvider = ({ client, children, fallback = null, ...rest }: Props) => {\n const [isInitializing, setIsInitializing] = useState<boolean>(true);\n\n useEffect(() => {\n client.init(rest).finally(() => {\n setIsInitializing(false);\n });\n // we want to init only on initial mount\n // oxlint-disable-next-line react/exhaustive-deps\n }, []);\n\n return isInitializing ? fallback : <Provider value={client}>{children}</Provider>;\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/ApiClientProvider.tsx","../src/AuthProvider.tsx","../src/LegacyAuthProvider.tsx"],"names":["jsx","useState","useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, use } from 'react';\n\nimport type { BearerSession, KeboolaApi, LegacySession } from '@keboola/api-client';\n\nconst MISSING_API_CLIENT_ERROR = 'useApiClient must be used within <ApiClientProvider />';\nconst MISSING_PROJECT_ERROR = 'useProject must be used within <ApiClientProvider />';\nconst MISSING_STACK_ERROR = 'useStack must be used within <ApiClientProvider />';\nconst MISSING_AUTH_ERROR = 'useAuth must be used within <AuthProvider />';\nconst MISSING_LEGACY_AUTH_ERROR = 'useLegacyAuth must be used within <LegacyAuthProvider />';\n\n// The product API (`auth` + `management` + every product namespace), flow-agnostic. Both the\n// bearer and legacy flows produce the same `KeboolaApi` surface, so consumers never branch on flow.\nconst ApiContext = createContext<KeboolaApi | null>(null);\nexport const ApiProvider = ApiContext;\n\n// The lifecycle sessions live in their own, flow-specific contexts so the auth surface a component\n// reaches for is explicit. Only the matching provider is mounted per flow.\nconst AuthContext = createContext<BearerSession | null>(null);\nexport const AuthContextProvider = AuthContext;\n\nconst LegacyAuthContext = createContext<LegacySession | null>(null);\nexport const LegacyAuthContextProvider = LegacyAuthContext;\n\nexport const useApiClient = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_API_CLIENT_ERROR);\n return api;\n};\n\nexport const useProject = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_PROJECT_ERROR);\n return api.context.project;\n};\n\nexport const useStack = () => {\n const api = use(ApiContext);\n if (api === null) throw new Error(MISSING_STACK_ERROR);\n return api.context.stack;\n};\n\n/** The bearer (OAuth) lifecycle session. Throws outside `<AuthProvider />`. */\nexport const useAuth = () => {\n const session = use(AuthContext);\n if (session === null) throw new Error(MISSING_AUTH_ERROR);\n return session;\n};\n\n/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */\nexport const useLegacyAuth = () => {\n const session = use(LegacyAuthContext);\n if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);\n return session;\n};\n","import type { ReactNode } from 'react';\n\nimport type { KeboolaApi } from '@keboola/api-client';\n\nimport { ApiProvider } from './context';\n\ntype Props = {\n /** The flow-agnostic product API — the `api` half of a keboola client. */\n api: KeboolaApi;\n children: ReactNode;\n};\n\n/**\n * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving\n * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount\n * this provider once their session is ready.\n */\nexport const ApiClientProvider = ({ api, children }: Props) => (\n <ApiProvider value={api}>{children}</ApiProvider>\n);\n","import { useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\nimport { AuthContextProvider } from './context';\n\ntype BearerCredential = Parameters<BearerSession['authenticate']>[0];\ntype Snapshot = ReturnType<BearerSession['debug']>;\ntype RestoreStatus = Awaited<ReturnType<BearerSession['restore']>>['status'];\n\n/** The session state guaranteed to exist once the storage build is ready. */\ntype ScopedState = { projectId: number; baseUrl: string };\n\ntype BaseProps = {\n session: BearerSession;\n onAccessTokenChange?: (accessToken: string) => void;\n /** Rendered while a persisted session is being restored, or the bootstrap is in flight. */\n loadingFallback?: ReactNode;\n /** Rendered when the storage build fails. */\n errorFallback?: ReactNode;\n /**\n * Rendered only once the session is scoped and the storage build is ready. Pass a function to\n * receive the scoped state (the project the session settled on) without re-narrowing it yourself.\n */\n children: ReactNode | ((scoped: ScopedState) => ReactNode);\n};\n\n/**\n * Two mutually exclusive bootstrap modes, discriminated by whether a credential is supplied:\n * - **programmatic** — `credential` + `projectId` given, so `baseUrl` is **required**: a credential\n * is bound to the stack that issued it and `authenticate` refuses an unconfigured session.\n * - **interactive** — none of the three given; the stack comes from the session instead — `restore()`\n * adopts the persisted one, otherwise the login UI sets it. `anonymousFallback` is **required**\n * here (a missing login UI would leave the app on a permanently blank screen).\n */\ntype Props = BaseProps &\n (\n | {\n credential: BearerCredential;\n projectId: number;\n baseUrl: string;\n anonymousFallback?: never;\n }\n | {\n credential?: undefined;\n projectId?: undefined;\n baseUrl?: never;\n anonymousFallback: ReactNode;\n }\n );\n\nexport const AuthProvider = ({\n session,\n baseUrl,\n credential,\n projectId,\n onAccessTokenChange,\n loadingFallback = null,\n errorFallback = null,\n anonymousFallback = null,\n children,\n}: Props) => {\n const [snapshot, setSnapshot] = useState<Snapshot>(() => session.debug());\n const [isRestoring, setIsRestoring] = useState(() => session.hasPersistedValue());\n const [restoreStatus, setRestoreStatus] = useState<RestoreStatus | null>(null);\n const restoreStartedRef = useRef(false);\n\n useEffect(() => session.subscribe(setSnapshot), [session]);\n\n // Programmatic only. Interactive leaves the stack unset: `restore()` adopts the persisted one, and\n // failing that the login UI picks it — so there is nothing to configure up front.\n useEffect(() => {\n if (baseUrl === undefined) return;\n if (snapshot.config.baseUrl !== null) return;\n session.setBaseUrl(baseUrl);\n }, [session, baseUrl, snapshot.config.baseUrl]);\n\n // Runs regardless of whether a stack is configured — with none, `restore()` adopts the stack the\n // persisted session belongs to. (Declared after the effect above, so programmatic mode has already\n // configured its stack by the time this runs.) The ref guards StrictMode's double-invoked effect:\n // `restoreStatus` only lands asynchronously, so it cannot prevent a second synchronous call.\n useEffect(() => {\n if (restoreStartedRef.current) return;\n restoreStartedRef.current = true;\n session\n .restore()\n .then((result) => setRestoreStatus(result.status))\n .catch((error) => {\n console.error('Keboola bearer session restore failed', error);\n setRestoreStatus('empty');\n })\n .finally(() => setIsRestoring(false));\n }, [session]);\n\n useEffect(() => {\n if (restoreStatus === null || restoreStatus === 'restored') return;\n if (credential === undefined) return;\n if (snapshot.config.status !== 'anonymous') return;\n session.authenticate(credential);\n }, [session, restoreStatus, credential, snapshot.config.status]);\n\n useEffect(() => {\n if (restoreStatus === null || restoreStatus === 'restored') return;\n if (projectId === undefined) return;\n if (snapshot.config.status !== 'authenticated') return;\n session.setProject(projectId);\n }, [session, restoreStatus, projectId, snapshot.config.status]);\n\n const accessToken =\n snapshot.config.status === 'anonymous' ? undefined : snapshot.config.credential.accessToken;\n\n useEffect(() => {\n if (accessToken !== undefined) onAccessTokenChange?.(accessToken);\n }, [accessToken, onAccessTokenChange]);\n\n // Programmatic mode drives itself to ready, so it must never fall through to `anonymousFallback`.\n const isProgrammatic = credential !== undefined && projectId !== undefined;\n\n const content = (() => {\n if (snapshot.storage === 'ready') {\n if (typeof children !== 'function') return children;\n // A ready storage build implies a scoped session; narrowing here is for the type checker.\n if (snapshot.config.status !== 'scoped') return null;\n const { projectId: scopedProjectId, baseUrl: scopedBaseUrl } = snapshot.config;\n return children({ projectId: scopedProjectId, baseUrl: scopedBaseUrl });\n }\n if (snapshot.storage === 'errored') return errorFallback;\n if (isRestoring || snapshot.storage === 'pending' || isProgrammatic) return loadingFallback;\n return anonymousFallback;\n })();\n\n return <AuthContextProvider value={session}>{content}</AuthContextProvider>;\n};\n","import { useEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport type { LegacySession } from '@keboola/api-client';\n\nimport { LegacyAuthContextProvider } from './context';\n\ntype Props = {\n session: LegacySession;\n /** Stack base URL. */\n baseUrl: string;\n /** SAPI token — drives the storage build to ready. */\n storageToken: string;\n /** Optional manage-api token — unlocks `api.management`. */\n managementToken?: string;\n /** Rendered until the storage build reaches ready. */\n fallback?: ReactNode;\n children: ReactNode;\n};\n\n/**\n * Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to\n * ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is\n * known up front, so this drives itself (unlike the interactive bearer `AuthProvider`). Exposes the\n * session via `useLegacyAuth()` and gates `children` behind readiness.\n */\nexport const LegacyAuthProvider = ({\n session,\n baseUrl,\n storageToken,\n managementToken,\n fallback = null,\n children,\n}: Props) => {\n const [isReady, setIsReady] = useState<boolean>(() => session.isStorageReady());\n // Guards the once-only drive against React StrictMode's double-mounted effect.\n const startedRef = useRef<boolean>(false);\n\n useEffect(() => {\n const unsubscribe = session.subscribe(() => setIsReady(session.isStorageReady()));\n\n if (!startedRef.current) {\n startedRef.current = true;\n session.setBaseUrl(baseUrl);\n if (managementToken) session.authenticateManagement(managementToken);\n // Fire-and-forget: `subscribe` flips `isReady` once the build resolves. A failure leaves the\n // fallback in place; log it so the bootstrap error isn't silent.\n void session\n .authenticateStorageAsync(storageToken)\n .catch((error) => console.error('Keboola session bootstrap failed', error));\n }\n\n return unsubscribe;\n // drive to ready only on initial mount\n // oxlint-disable-next-line react/exhaustive-deps\n }, []);\n\n return (\n <LegacyAuthContextProvider value={session}>\n {isReady ? children : fallback}\n </LegacyAuthContextProvider>\n );\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keboola/api-client-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"description": "React bindings for @keboola/api-client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"react": "^19.0.0",
|
|
52
|
-
"@keboola/api-client": "^
|
|
52
|
+
"@keboola/api-client": "^21.0.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"tsup": "^8.5.1",
|
|
71
71
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
72
72
|
"vite": "^8.0.16",
|
|
73
|
-
"vitest": "^4.1.
|
|
74
|
-
"@keboola/api-client": "^20.0.0",
|
|
73
|
+
"vitest": "^4.1.10",
|
|
75
74
|
"@keboola/oxlint-config": "^8.0.0",
|
|
76
|
-
"@keboola/tsconfig": "^0.2.0"
|
|
75
|
+
"@keboola/tsconfig": "^0.2.0",
|
|
76
|
+
"@keboola/api-client": "^21.0.0"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"type-check": "tsc",
|