@keboola/api-client-react 48.0.0 → 50.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/AGENTS.md +47 -77
- package/CHANGELOG.md +34 -0
- package/README.md +8 -9
- package/dist/index.cjs +43 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -47
- package/dist/index.d.ts +19 -47
- package/dist/index.js +39 -30
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @keboola/api-client-react
|
|
2
2
|
|
|
3
3
|
React bindings for [`@keboola/api-client`](../api-client/AGENTS.md). Owns
|
|
4
|
-
the
|
|
5
|
-
consumers to read
|
|
4
|
+
the contexts that hold the typed `StackApi` and `ProjectApi` and the hooks for
|
|
5
|
+
consumers to read them.
|
|
6
6
|
|
|
7
7
|
## Why a separate package
|
|
8
8
|
|
|
@@ -15,77 +15,46 @@ non-React surfaces. This package is the **only** place that knows about
|
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
17
|
import {
|
|
18
|
-
ApiClientProvider,
|
|
19
|
-
AuthProvider,
|
|
20
18
|
LegacyAuthProvider,
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
ProjectApiProvider,
|
|
20
|
+
StackApiProvider,
|
|
21
|
+
useActiveStack,
|
|
23
22
|
useLegacyAuth,
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
useProjectApi,
|
|
24
|
+
useSessionState,
|
|
25
|
+
useStackApi,
|
|
26
26
|
} from '@keboola/api-client-react';
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- `
|
|
32
|
-
|
|
33
|
-
- `
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"Not yet scoped" is **not** the same as "signed out": the storage build sits at `idle` both for an
|
|
55
|
-
anonymous visitor and for an authenticated one with no project selected (zero projects, or just
|
|
56
|
-
used "Leave project"), and both land in `anonymousFallback`. Consumers that need to tell the two
|
|
57
|
-
apart have to read the session status themselves.
|
|
58
|
-
|
|
59
|
-
`children` may be a function — it is called only in the ready state and receives the scoped
|
|
60
|
-
`{ projectId, baseUrl }`, so consumers don't re-narrow the session state themselves:
|
|
61
|
-
|
|
62
|
-
```tsx
|
|
63
|
-
<AuthProvider session={session} baseUrl={url} anonymousFallback={<Login />}>
|
|
64
|
-
{({ projectId }) => <App projectId={projectId} />}
|
|
65
|
-
</AuthProvider>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
`onAccessTokenChange` fires with the access token on the initial credential and after every
|
|
69
|
-
in-band rotation, so the host can mirror it (e.g. into a token store). Exposes the session via
|
|
70
|
-
`useAuth()`.
|
|
71
|
-
|
|
72
|
-
`onRestore` fires once with the boot `restore()`'s `{ status }`. The slot that renders cannot
|
|
73
|
-
stand in for it — `expired` and `empty` both land in `anonymousFallback`, and so does a `restored`
|
|
74
|
-
session that carried no project — so an app that wants to say "your session expired" rather than
|
|
75
|
-
show a bare sign-in screen needs this callback. A `restore()` that throws reports `empty`, the
|
|
76
|
-
same value the provider acts on, so a transport failure is never reported as an expiry.
|
|
77
|
-
|
|
78
|
-
- `useApiClient()` — returns the `api`; **throws** outside `<ApiClientProvider />`. Use it from
|
|
79
|
-
query/mutation hooks.
|
|
80
|
-
- `useProject()` / `useStack()` — shortcut hooks for `api.context.project` / `api.context.stack`.
|
|
81
|
-
Same null-throwing contract.
|
|
82
|
-
|
|
83
|
-
Nesting: mount the auth provider outermost (it owns drive-to-ready) with `ApiClientProvider` inside
|
|
84
|
-
it, so product code only mounts once the `api` is ready:
|
|
29
|
+
Two handles, one per scope, each with its provider and hook:
|
|
30
|
+
|
|
31
|
+
- `StackApiProvider` / `useStackApi()` — a `StackApi`: `auth`, `management`, `baseUrl`, `stack`.
|
|
32
|
+
What is there before a project — sign-in, the account, the project list.
|
|
33
|
+
- `ProjectApiProvider` / `useProjectApi()` — a `ProjectApi`: a `StackApi` plus every project
|
|
34
|
+
namespace. It provides its stack too, so `useStackApi()` answers inside a project.
|
|
35
|
+
- The verified token and the default branch are the app's to fetch (`api.storage.tokens.verify()`,
|
|
36
|
+
`api.storage.branches.getDevBranches()`) and derive with `createProject` / `createDefaultBranch`.
|
|
37
|
+
- `LegacyAuthProvider` — takes a **legacy** `session` (+ `baseUrl`, `storageToken`, optional
|
|
38
|
+
`managementToken`). On mount it drives the session to ready (`setBaseUrl` →
|
|
39
|
+
`authenticateManagement?` → `authenticateStorageAsync`) and renders `fallback` until
|
|
40
|
+
`session.isStorageReady()`. Exposes the session via `useLegacyAuth()`.
|
|
41
|
+
- `useSessionState(session)` / `useActiveStack(session)` — a bearer session's table, and its active
|
|
42
|
+
stack, as re-rendering values.
|
|
43
|
+
|
|
44
|
+
Every hook **throws** outside its provider. Providers are pure pass-throughs: no lifecycle, no
|
|
45
|
+
fallback. Mount the stack where the app is signed in and the project where the URL names one:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<StackApiProvider api={keboola.getStackApi()}>
|
|
49
|
+
<ProjectApiProvider api={keboola.getProjectApi(projectId)}>{children}</ProjectApiProvider>
|
|
50
|
+
</StackApiProvider>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A legacy client is one project: its `api` is a `ProjectApi`, mounted inside `LegacyAuthProvider`:
|
|
85
54
|
|
|
86
55
|
```tsx
|
|
87
56
|
<LegacyAuthProvider session={session} baseUrl={url} storageToken={token} fallback={<Loading />}>
|
|
88
|
-
<
|
|
57
|
+
<ProjectApiProvider api={api}>{children}</ProjectApiProvider>
|
|
89
58
|
</LegacyAuthProvider>
|
|
90
59
|
```
|
|
91
60
|
|
|
@@ -97,19 +66,18 @@ verify: `createStorageClient({ baseUrl, auth: { type: 'sapi-token', token } }).t
|
|
|
97
66
|
|
|
98
67
|
## Conventions
|
|
99
68
|
|
|
100
|
-
- The context
|
|
101
|
-
null — see [apps/AGENTS.md](../../apps/AGENTS.md) ("State Management"
|
|
102
|
-
|
|
103
|
-
|
|
69
|
+
- The context values are typed `StackApi | null` and `ProjectApi | null`, and the hooks throw on
|
|
70
|
+
null — see [apps/AGENTS.md](../../apps/AGENTS.md) ("State Management" → "Tree-passing"). Don't
|
|
71
|
+
soften this to optional types or `??` fallbacks; a missing provider is a programming error.
|
|
72
|
+
- Reach for `useStackApi()` when the code only needs the stack: it then works before a project too.
|
|
104
73
|
- React is a **peerDependency** (^19.0.0). Don't import React types from
|
|
105
74
|
this package's own deps in consumers.
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
mount a fresh provider.
|
|
75
|
+
- `LegacyAuthProvider` drives the session to ready exactly once on mount. Changing its props after
|
|
76
|
+
mount is a no-op by design — if you need a new auth context, mount a fresh provider.
|
|
109
77
|
|
|
110
78
|
## Non-component access (loaders, services, module scope)
|
|
111
79
|
|
|
112
|
-
`
|
|
80
|
+
`useStackApi()` and `useProjectApi()` are for React components only. A TanStack Router loader, a
|
|
113
81
|
module instantiated at import time, or any code running outside the React tree
|
|
114
82
|
can't call the hook. Don't reach for the context there — construct a client
|
|
115
83
|
directly:
|
|
@@ -130,15 +98,17 @@ re-creating clients per call. See
|
|
|
130
98
|
|
|
131
99
|
## Canonical consumers
|
|
132
100
|
|
|
133
|
-
- `apps/kbc-ui/src/scripts/
|
|
134
|
-
|
|
101
|
+
- `apps/kbc-ui/src/scripts/react/layout/AppRouter/components/SessionProviders.tsx` — the
|
|
102
|
+
production `StackApiProvider` mount; `login/pages/ProjectPage.tsx` mounts `ProjectApiProvider`
|
|
103
|
+
from the URL's project
|
|
104
|
+
- `apps/kbc-ui/src/scripts/modules/sandboxes/queries.ts` — `useProjectApi`
|
|
135
105
|
inside a TanStack Query hook (the dominant pattern)
|
|
136
106
|
|
|
137
107
|
## Don'ts
|
|
138
108
|
|
|
139
109
|
- **Never** create a parallel React context that wraps another client
|
|
140
110
|
instance. One client per app, mounted once at the top.
|
|
141
|
-
- **Never** drive `session` manually in a consumer —
|
|
142
|
-
|
|
111
|
+
- **Never** drive a legacy `session` manually in a consumer — `LegacyAuthProvider` owns the
|
|
112
|
+
drive-to-ready lifecycle.
|
|
143
113
|
- **Never** import from a deep path (`@keboola/api-client-react/src/...`).
|
|
144
114
|
The package only exports `src/index.ts`.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @keboola/api-client-react
|
|
2
2
|
|
|
3
|
+
## 50.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- The SDK no longer fetches a project's state. `api.baseUrl` and `api.stack` replace `api.context`; the verified token and the dev branches are the consumer's to fetch (`api.storage.tokens.verify()`, `api.storage.branches.getDevBranches()`) and derive with the exported `createProject` / `createDefaultBranch`.
|
|
8
|
+
- **Breaking:** `api.context` is gone. `api.baseUrl` is the canonical stack URL — on a bearer handle readable before a sign-in; `api.stack` is the parsed stack index, `NotReady` until it lands. The `ClientContext` type is replaced by `Stack`.
|
|
9
|
+
- **Breaking:** the project context (`sapiToken`, `defaultBranch`) is gone, with `session.project.getContext` / `getContextAsync` / `retryContext` and the `context` status on a project row. `api.sdk.configuration` is gone — build it with `createConfigurationSdk` from `@keboola/api-client/sdk/configurations`, handing it the project and default branch.
|
|
10
|
+
- **Breaking:** the bearer session knows no project; which one the app is in is the app's to keep. `getState().active` is the active stack's `baseUrl`. `session.project` (`select`, `deactivate`, `close`), `getState().projects` and the `ProjectEntry` and `ActiveLocation` types are gone, and `resume()` restores the stack alone.
|
|
11
|
+
- **Breaking:** the flat `KeboolaApi` splits in two. `StackApi` (`auth`, `management`, `baseUrl`, `stack`) is what is there before a project; `ProjectApi` is a `StackApi` plus every project namespace. `createKeboola` returns `getStackApi()` — the active stack, re-pointed by `session.stack.select` — and `getProjectApi(projectId)` in place of `getActiveApi` / `getApi`; `createLegacyKeboola(...).api` is a `ProjectApi`.
|
|
12
|
+
- **Breaking:** `getProjectApi(projectId)` writes and persists nothing, lives as long as the sign-in it was built under, and throws `NotAuthenticated` before one lands. A handle read after its sign-in ended throws `KeboolaError` (`SignInEnded`).
|
|
13
|
+
- **Breaking:** `auth.getHeadersAsync(projectId)` requires the project, resolved on the active stack. The synchronous `auth.getHeaders` is gone.
|
|
14
|
+
- **Breaking:** `session.stack.close`, `session.stack.retryContext`, `session.auth.rotate` and the `StackActive` error type are gone. A stack, once opened, stays in the table; `stack.getContextAsync` fetches a failed stack index again.
|
|
15
|
+
- The persisted session still writes `current.projectId: null` and an empty `projects` per stack, so an older build reading it does not sign out.
|
|
16
|
+
- **Breaking:** `@keboola/api-client-react` replaces `ApiClientProvider` / `useApiClient` with `StackApiProvider` / `useStackApi` and `ProjectApiProvider` / `useProjectApi`; a `ProjectApiProvider` provides its stack too. `useStack` and `useProject` are gone; `useStackApi().stack` reads the stack.
|
|
17
|
+
- **Breaking:** `createLegacyKeboola`'s storage build reads the stack index only; it no longer verifies the SAPI token, so a ready session does not mean a live token. Verify it with `api.storage.tokens.verify()` where a dead token must be caught.
|
|
18
|
+
- **Breaking:** the storage client no longer re-verifies the token after a bucket or feature write, and `isStorageTokenMutatingRequest` is no longer exported; the predicate moved to kbc-ui beside its refresh middleware.
|
|
19
|
+
- `api.storage` under the bearer flow is live from the sign-in on.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies:
|
|
24
|
+
- @keboola/api-client@50.0.0
|
|
25
|
+
|
|
26
|
+
## 49.0.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- `useActiveStack(session)` returns the active stack's row as a re-rendering value, the subscribed form of `session.stack.getActive()` (UT-5890)
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Updated dependencies:
|
|
35
|
+
- @keboola/api-client@49.0.0
|
|
36
|
+
|
|
3
37
|
## 48.0.0
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -14,11 +14,11 @@ Peer dependencies: `react ^19.0.0`, `@keboola/api-client`.
|
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Two handles: a `StackApi` (what is there before a project) and a `ProjectApi` (a stack plus every project namespace). Legacy (SAPI) shown here — its `api` is one project, mounted inside `LegacyAuthProvider`, which drives the `session` to ready and renders `fallback` until then. Bearer mounts `StackApiProvider` with `createKeboola(...).getStackApi()` and `ProjectApiProvider` with `getProjectApi(projectId)`.
|
|
18
18
|
|
|
19
19
|
```tsx
|
|
20
20
|
import { createLegacyKeboola } from '@keboola/api-client';
|
|
21
|
-
import {
|
|
21
|
+
import { LegacyAuthProvider, ProjectApiProvider, useProjectApi } from '@keboola/api-client-react';
|
|
22
22
|
|
|
23
23
|
const { api, session } = createLegacyKeboola();
|
|
24
24
|
|
|
@@ -29,14 +29,14 @@ export const App = () => (
|
|
|
29
29
|
storageToken="your-storage-token"
|
|
30
30
|
fallback={<div>Loading…</div>}
|
|
31
31
|
>
|
|
32
|
-
<
|
|
32
|
+
<ProjectApiProvider api={api}>
|
|
33
33
|
<Tables />
|
|
34
|
-
</
|
|
34
|
+
</ProjectApiProvider>
|
|
35
35
|
</LegacyAuthProvider>
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
const Tables = () => {
|
|
39
|
-
const api =
|
|
39
|
+
const api = useProjectApi();
|
|
40
40
|
// api.storage.tables.getTables(), api.queue.searchJobs(), etc.
|
|
41
41
|
return null;
|
|
42
42
|
};
|
|
@@ -44,10 +44,9 @@ const Tables = () => {
|
|
|
44
44
|
|
|
45
45
|
## Exported hooks
|
|
46
46
|
|
|
47
|
-
- `
|
|
48
|
-
- `
|
|
49
|
-
- `
|
|
50
|
-
- `useLegacyAuth()` / `useAuth()` — the flow-specific `session` (legacy / bearer).
|
|
47
|
+
- `useProjectApi()` — the provided `ProjectApi` (`auth` + `management` + every project namespace).
|
|
48
|
+
- `useStackApi()` — the provided `StackApi` (`auth`, `management`, `baseUrl`, `stack`); a `ProjectApiProvider` provides its stack too.
|
|
49
|
+
- `useLegacyAuth()` — the legacy `session`; `useSessionState(session)` reads a bearer one.
|
|
51
50
|
|
|
52
51
|
## Ownership
|
|
53
52
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let react = require("react");
|
|
3
3
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const MISSING_STACK_ERROR = "useStack must be used within <ApiClientProvider />";
|
|
4
|
+
// src/context.ts
|
|
5
|
+
const MISSING_STACK_API_ERROR = "useStackApi must be used within <StackApiProvider />";
|
|
6
|
+
const MISSING_PROJECT_API_ERROR = "useProjectApi must be used within <ProjectApiProvider />";
|
|
8
7
|
const MISSING_LEGACY_AUTH_ERROR = "useLegacyAuth must be used within <LegacyAuthProvider />";
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* One stack: what is there before a project — signing in, the account, the project list.
|
|
10
|
+
*
|
|
11
|
+
* - A `ProjectApiProvider` provides its project's stack here too.
|
|
12
|
+
*/
|
|
13
|
+
const StackApiContext = (0, react.createContext)(null);
|
|
14
|
+
const StackApiContextProvider = StackApiContext;
|
|
15
|
+
const ProjectApiContext = (0, react.createContext)(null);
|
|
16
|
+
const ProjectApiContextProvider = ProjectApiContext;
|
|
11
17
|
const LegacyAuthContext = (0, react.createContext)(null);
|
|
12
18
|
const LegacyAuthContextProvider = LegacyAuthContext;
|
|
13
|
-
const
|
|
14
|
-
const api = (0, react.use)(
|
|
15
|
-
if (api === null) throw new Error(
|
|
19
|
+
const useStackApi = () => {
|
|
20
|
+
const api = (0, react.use)(StackApiContext);
|
|
21
|
+
if (api === null) throw new Error(MISSING_STACK_API_ERROR);
|
|
16
22
|
return api;
|
|
17
23
|
};
|
|
18
|
-
const
|
|
19
|
-
const api = (0, react.use)(
|
|
20
|
-
if (api === null) throw new Error(
|
|
21
|
-
return api
|
|
22
|
-
};
|
|
23
|
-
const useStack = () => {
|
|
24
|
-
const api = (0, react.use)(ApiContext);
|
|
25
|
-
if (api === null) throw new Error(MISSING_STACK_ERROR);
|
|
26
|
-
return api.context.stack;
|
|
24
|
+
const useProjectApi = () => {
|
|
25
|
+
const api = (0, react.use)(ProjectApiContext);
|
|
26
|
+
if (api === null) throw new Error(MISSING_PROJECT_API_ERROR);
|
|
27
|
+
return api;
|
|
27
28
|
};
|
|
28
29
|
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
29
30
|
const useLegacyAuth = () => {
|
|
@@ -31,23 +32,32 @@ const useLegacyAuth = () => {
|
|
|
31
32
|
if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
|
|
32
33
|
return session;
|
|
33
34
|
};
|
|
34
|
-
|
|
35
|
-
//#region src/ApiClientProvider.tsx
|
|
35
|
+
// src/ProjectApiProvider.tsx
|
|
36
36
|
/**
|
|
37
|
-
* Provides
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.
|
|
38
|
+
*
|
|
39
|
+
* - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.
|
|
40
40
|
*/
|
|
41
|
-
const
|
|
41
|
+
const ProjectApiProvider = ({ api, children }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackApiContextProvider, {
|
|
42
|
+
value: api,
|
|
43
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectApiContextProvider, {
|
|
44
|
+
value: api,
|
|
45
|
+
children
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
// src/StackApiProvider.tsx
|
|
49
|
+
/** Provides one stack to `useStackApi()`. */
|
|
50
|
+
const StackApiProvider = ({ api, children }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackApiContextProvider, {
|
|
42
51
|
value: api,
|
|
43
52
|
children
|
|
44
53
|
});
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
// src/useActiveStack.ts
|
|
55
|
+
/** The active stack's row as a re-rendering value; the default stack until something selects another. */
|
|
56
|
+
const useActiveStack = (session) => (0, react.useSyncExternalStore)(session.subscribe, session.stack.getActive);
|
|
57
|
+
// src/useSessionState.ts
|
|
47
58
|
/** The session table as a re-rendering value — the binding every gate and panel reads it through. */
|
|
48
59
|
const useSessionState = (session) => (0, react.useSyncExternalStore)(session.subscribe, session.getState);
|
|
49
|
-
|
|
50
|
-
//#region src/LegacyAuthProvider.tsx
|
|
60
|
+
// src/LegacyAuthProvider.tsx
|
|
51
61
|
/**
|
|
52
62
|
* Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
|
|
53
63
|
* ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
|
|
@@ -79,13 +89,13 @@ const LegacyAuthProvider = ({ session, baseUrl, storageToken, managementToken, f
|
|
|
79
89
|
})()
|
|
80
90
|
});
|
|
81
91
|
};
|
|
82
|
-
//#endregion
|
|
83
|
-
exports.ApiClientProvider = ApiClientProvider;
|
|
84
92
|
exports.LegacyAuthProvider = LegacyAuthProvider;
|
|
85
|
-
exports.
|
|
93
|
+
exports.ProjectApiProvider = ProjectApiProvider;
|
|
94
|
+
exports.StackApiProvider = StackApiProvider;
|
|
95
|
+
exports.useActiveStack = useActiveStack;
|
|
86
96
|
exports.useLegacyAuth = useLegacyAuth;
|
|
87
|
-
exports.
|
|
97
|
+
exports.useProjectApi = useProjectApi;
|
|
88
98
|
exports.useSessionState = useSessionState;
|
|
89
|
-
exports.
|
|
99
|
+
exports.useStackApi = useStackApi;
|
|
90
100
|
|
|
91
101
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["createContext","use","useSyncExternalStore","useState","useRef"],"sources":["../src/context.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createContext","use","useSyncExternalStore","useSyncExternalStore","useState","useRef"],"sources":["../src/context.ts","../src/ProjectApiProvider.tsx","../src/StackApiProvider.tsx","../src/useActiveStack.ts","../src/useSessionState.ts","../src/LegacyAuthProvider.tsx"],"sourcesContent":["import { createContext, use } from 'react';\n\nimport type { LegacySession, ProjectApi, StackApi } from '@keboola/api-client';\n\nconst MISSING_STACK_API_ERROR = 'useStackApi must be used within <StackApiProvider />';\nconst MISSING_PROJECT_API_ERROR = 'useProjectApi must be used within <ProjectApiProvider />';\nconst MISSING_LEGACY_AUTH_ERROR = 'useLegacyAuth must be used within <LegacyAuthProvider />';\n\n/**\n * One stack: what is there before a project — signing in, the account, the project list.\n *\n * - A `ProjectApiProvider` provides its project's stack here too.\n */\nconst StackApiContext = createContext<StackApi | null>(null);\nexport const StackApiContextProvider = StackApiContext;\n\n// One project, on its stack. Both flows produce the same `ProjectApi`, so consumers never branch on\n// flow.\nconst ProjectApiContext = createContext<ProjectApi | null>(null);\nexport const ProjectApiContextProvider = ProjectApiContext;\n\n// The legacy lifecycle session lives in its own context; the bearer flows read their table through\n// `useSessionState` instead.\nconst LegacyAuthContext = createContext<LegacySession | null>(null);\nexport const LegacyAuthContextProvider = LegacyAuthContext;\n\nexport const useStackApi = () => {\n const api = use(StackApiContext);\n if (api === null) throw new Error(MISSING_STACK_API_ERROR);\n return api;\n};\n\nexport const useProjectApi = () => {\n const api = use(ProjectApiContext);\n if (api === null) throw new Error(MISSING_PROJECT_API_ERROR);\n return api;\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 { ProjectApi } from '@keboola/api-client';\n\nimport { ProjectApiContextProvider, StackApiContextProvider } from './context';\n\ntype Props = {\n api: ProjectApi;\n children: ReactNode;\n};\n\n/**\n * Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.\n *\n * - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.\n */\nexport const ProjectApiProvider = ({ api, children }: Props) => (\n <StackApiContextProvider value={api}>\n <ProjectApiContextProvider value={api}>{children}</ProjectApiContextProvider>\n </StackApiContextProvider>\n);\n","import type { ReactNode } from 'react';\n\nimport type { StackApi } from '@keboola/api-client';\n\nimport { StackApiContextProvider } from './context';\n\ntype Props = {\n api: StackApi;\n children: ReactNode;\n};\n\n/** Provides one stack to `useStackApi()`. */\nexport const StackApiProvider = ({ api, children }: Props) => (\n <StackApiContextProvider value={api}>{children}</StackApiContextProvider>\n);\n","import { useSyncExternalStore } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\n/** The active stack's row as a re-rendering value; the default stack until something selects another. */\nexport const useActiveStack = (session: BearerSession) =>\n useSyncExternalStore(session.subscribe, session.stack.getActive);\n","import { useSyncExternalStore } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\n/** The session table as a re-rendering value — the binding every gate and panel reads it through. */\nexport const useSessionState = (session: BearerSession) =>\n useSyncExternalStore(session.subscribe, session.getState);\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 /** Rendered when the storage build fails — the stack index could not be read. */\n errorFallback?: 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 errorFallback = fallback,\n children,\n}: Props) => {\n // The build state, not just readiness: `errored` and `pending` render differently.\n const [storageState, setStorageState] = useState(() => session.debug().storage);\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((snapshot) => setStorageState(snapshot.storage));\n\n if (!startedRef.current) {\n startedRef.current = true;\n session.setBaseUrl(baseUrl);\n if (managementToken) session.authenticateManagement(managementToken);\n // Fire-and-forget: `subscribe` moves `storageState` once the build resolves. A failed build\n // resolves rather than rejects, so this catch only covers a synchronous throw.\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 // The only place a failed build is observable — nothing rejects, so without this it is silent.\n useEffect(() => {\n if (storageState === 'errored') {\n console.error('Keboola session bootstrap failed: the storage build errored.');\n }\n }, [storageState]);\n\n const content = (() => {\n if (storageState === 'ready') return children;\n if (storageState === 'errored') return errorFallback;\n return fallback;\n })();\n\n return <LegacyAuthContextProvider value={session}>{content}</LegacyAuthContextProvider>;\n};\n"],"mappings":";;;;AAIA,MAAM,0BAA0B;AAChC,MAAM,4BAA4B;AAClC,MAAM,4BAA4B;;;;;;AAOlC,MAAM,mBAAA,GAAkBA,MAAAA,cAAAA,CAA+B,IAAI;AAC3D,MAAa,0BAA0B;AAIvC,MAAM,qBAAA,GAAoBA,MAAAA,cAAAA,CAAiC,IAAI;AAC/D,MAAa,4BAA4B;AAIzC,MAAM,qBAAA,GAAoBA,MAAAA,cAAAA,CAAoC,IAAI;AAClE,MAAa,4BAA4B;AAEzC,MAAa,oBAAoB;CAC/B,MAAM,OAAA,GAAMC,MAAAA,IAAAA,CAAI,eAAe;CAC/B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,uBAAuB;CACzD,OAAO;AACT;AAEA,MAAa,sBAAsB;CACjC,MAAM,OAAA,GAAMA,MAAAA,IAAAA,CAAI,iBAAiB;CACjC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC3D,OAAO;AACT;;AAGA,MAAa,sBAAsB;CACjC,MAAM,WAAA,GAAUA,MAAAA,IAAAA,CAAI,iBAAiB;CACrC,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC/D,OAAO;AACT;;;;;;;AC3BA,MAAa,sBAAsB,EAAE,KAAK,eACxC,iBAAA,GAAA,kBAAA,IAAA,CAAC,yBAAD;CAAyB,OAAO;CAC9B,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,2BAAD;EAA2B,OAAO;EAAM;CAAoC,CAAA;AACrD,CAAA;;;ACP3B,MAAa,oBAAoB,EAAE,KAAK,eACtC,iBAAA,GAAA,kBAAA,IAAA,CAAC,yBAAD;CAAyB,OAAO;CAAM;AAAkC,CAAA;;;ACR1E,MAAa,kBAAkB,aAAA,GAC7BC,MAAAA,qBAAAA,CAAqB,QAAQ,WAAW,QAAQ,MAAM,SAAS;;;ACDjE,MAAa,mBAAmB,aAAA,GAC9BC,MAAAA,qBAAAA,CAAqB,QAAQ,WAAW,QAAQ,QAAQ;;;;;;;;ACsB1D,MAAa,sBAAsB,EACjC,SACA,SACA,cACA,iBACA,WAAW,MACX,gBAAgB,UAChB,eACW;CAEX,MAAM,CAAC,cAAc,oBAAA,GAAmBC,MAAAA,SAAAA,OAAe,QAAQ,MAAM,CAAC,CAAC,OAAO;CAE9E,MAAM,cAAA,GAAaC,MAAAA,OAAAA,CAAgB,KAAK;CAExC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,cAAc,QAAQ,WAAW,aAAa,gBAAgB,SAAS,OAAO,CAAC;EAErF,IAAI,CAAC,WAAW,SAAS;GACvB,WAAW,UAAU;GACrB,QAAQ,WAAW,OAAO;GAC1B,IAAI,iBAAiB,QAAQ,uBAAuB,eAAe;GAGnE,QACG,yBAAyB,YAAY,CAAC,CACtC,OAAO,UAAU,QAAQ,MAAM,oCAAoC,KAAK,CAAC;EAC9E;EAEA,OAAO;CAGT,GAAG,CAAC,CAAC;CAGL,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,iBAAiB,WACnB,QAAQ,MAAM,8DAA8D;CAEhF,GAAG,CAAC,YAAY,CAAC;CAQjB,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,2BAAD;EAA2B,OAAO;EANlB,iBAAA;GACrB,IAAI,iBAAiB,SAAS,OAAO;GACrC,IAAI,iBAAiB,WAAW,OAAO;GACvC,OAAO;EACT,EAAA,CAEyD;CAA6B,CAAA;AACxF"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,35 +1,7 @@
|
|
|
1
|
-
import { BearerSession,
|
|
1
|
+
import { BearerSession, LegacySession, ProjectApi, StackApi } from "@keboola/api-client";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
-
|
|
4
|
-
declare const
|
|
5
|
-
declare const useProject: () => {
|
|
6
|
-
hasFeature: (...feature: string[]) => boolean;
|
|
7
|
-
hasAdminFeature: (...feature: string[]) => boolean;
|
|
8
|
-
hasBackend: (backend: import("@keboola/api-client/constants").Backend) => boolean;
|
|
9
|
-
sapiToken: import("@keboola/api-client/storage/types").StorageToken;
|
|
10
|
-
};
|
|
11
|
-
declare const useStack: () => {
|
|
12
|
-
stack: string;
|
|
13
|
-
host: string;
|
|
14
|
-
api: string;
|
|
15
|
-
version: string;
|
|
16
|
-
revision: string;
|
|
17
|
-
documentation: string;
|
|
18
|
-
urlTemplates: {
|
|
19
|
-
orchestrationJob: string;
|
|
20
|
-
};
|
|
21
|
-
services: {
|
|
22
|
-
id: string;
|
|
23
|
-
url: string;
|
|
24
|
-
}[];
|
|
25
|
-
features: string[];
|
|
26
|
-
components: import("@keboola/api-client/storage/types").StackComponent[];
|
|
27
|
-
hasFeature: (...feature: string[]) => boolean;
|
|
28
|
-
getServiceUrl: (serviceId: import("@keboola/api-client/constants").ServiceId) => string | null;
|
|
29
|
-
hasService: (serviceId: import("@keboola/api-client/constants").ServiceId) => boolean;
|
|
30
|
-
getComponent: (componentId: string) => import("@keboola/api-client/storage/types").StackComponent | null;
|
|
31
|
-
hasComponent: (...componentId: string[]) => boolean;
|
|
32
|
-
};
|
|
3
|
+
declare const useStackApi: () => StackApi;
|
|
4
|
+
declare const useProjectApi: () => ProjectApi;
|
|
33
5
|
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
34
6
|
declare const useLegacyAuth: () => {
|
|
35
7
|
setBaseUrl: (baseUrl: string) => void;
|
|
@@ -60,25 +32,26 @@ declare const useLegacyAuth: () => {
|
|
|
60
32
|
storage: import("@keboola/api-client").AsyncState<unknown>['state'];
|
|
61
33
|
};
|
|
62
34
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
type Props$1 = {
|
|
66
|
-
/** The flow-agnostic product API — the `api` half of a keboola client. */
|
|
67
|
-
api: KeboolaApi;
|
|
35
|
+
type Props$2 = {
|
|
36
|
+
api: ProjectApi;
|
|
68
37
|
children: ReactNode;
|
|
69
38
|
};
|
|
70
39
|
/**
|
|
71
|
-
* Provides
|
|
72
|
-
*
|
|
73
|
-
*
|
|
40
|
+
* Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.
|
|
41
|
+
*
|
|
42
|
+
* - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.
|
|
74
43
|
*/
|
|
75
|
-
declare const
|
|
76
|
-
|
|
77
|
-
|
|
44
|
+
declare const ProjectApiProvider: ({ api, children }: Props$2) => import("react").JSX.Element;
|
|
45
|
+
type Props$1 = {
|
|
46
|
+
api: StackApi;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
};
|
|
49
|
+
/** Provides one stack to `useStackApi()`. */
|
|
50
|
+
declare const StackApiProvider: ({ api, children }: Props$1) => import("react").JSX.Element;
|
|
51
|
+
/** The active stack's row as a re-rendering value; the default stack until something selects another. */
|
|
52
|
+
declare const useActiveStack: (session: BearerSession) => import("@keboola/api-client").StackEntry;
|
|
78
53
|
/** The session table as a re-rendering value — the binding every gate and panel reads it through. */
|
|
79
54
|
declare const useSessionState: (session: BearerSession) => import("@keboola/api-client").SessionState;
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/LegacyAuthProvider.d.ts
|
|
82
55
|
type Props = {
|
|
83
56
|
session: LegacySession;
|
|
84
57
|
/** Stack base URL. */
|
|
@@ -89,7 +62,7 @@ type Props = {
|
|
|
89
62
|
managementToken?: string;
|
|
90
63
|
/** Rendered until the storage build reaches ready. */
|
|
91
64
|
fallback?: ReactNode;
|
|
92
|
-
/** Rendered when the storage build fails —
|
|
65
|
+
/** Rendered when the storage build fails — the stack index could not be read. */
|
|
93
66
|
errorFallback?: ReactNode;
|
|
94
67
|
children: ReactNode;
|
|
95
68
|
};
|
|
@@ -100,6 +73,5 @@ type Props = {
|
|
|
100
73
|
* session via `useLegacyAuth()` and gates `children` behind readiness.
|
|
101
74
|
*/
|
|
102
75
|
declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, errorFallback, children }: Props) => import("react").JSX.Element;
|
|
103
|
-
|
|
104
|
-
export { ApiClientProvider, LegacyAuthProvider, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
|
|
76
|
+
export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
|
|
105
77
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { BearerSession,
|
|
3
|
-
|
|
4
|
-
declare const
|
|
5
|
-
declare const useProject: () => {
|
|
6
|
-
hasFeature: (...feature: string[]) => boolean;
|
|
7
|
-
hasAdminFeature: (...feature: string[]) => boolean;
|
|
8
|
-
hasBackend: (backend: import("@keboola/api-client/constants").Backend) => boolean;
|
|
9
|
-
sapiToken: import("@keboola/api-client/storage/types").StorageToken;
|
|
10
|
-
};
|
|
11
|
-
declare const useStack: () => {
|
|
12
|
-
stack: string;
|
|
13
|
-
host: string;
|
|
14
|
-
api: string;
|
|
15
|
-
version: string;
|
|
16
|
-
revision: string;
|
|
17
|
-
documentation: string;
|
|
18
|
-
urlTemplates: {
|
|
19
|
-
orchestrationJob: string;
|
|
20
|
-
};
|
|
21
|
-
services: {
|
|
22
|
-
id: string;
|
|
23
|
-
url: string;
|
|
24
|
-
}[];
|
|
25
|
-
features: string[];
|
|
26
|
-
components: import("@keboola/api-client/storage/types").StackComponent[];
|
|
27
|
-
hasFeature: (...feature: string[]) => boolean;
|
|
28
|
-
getServiceUrl: (serviceId: import("@keboola/api-client/constants").ServiceId) => string | null;
|
|
29
|
-
hasService: (serviceId: import("@keboola/api-client/constants").ServiceId) => boolean;
|
|
30
|
-
getComponent: (componentId: string) => import("@keboola/api-client/storage/types").StackComponent | null;
|
|
31
|
-
hasComponent: (...componentId: string[]) => boolean;
|
|
32
|
-
};
|
|
2
|
+
import { BearerSession, LegacySession, ProjectApi, StackApi } from "@keboola/api-client";
|
|
3
|
+
declare const useStackApi: () => StackApi;
|
|
4
|
+
declare const useProjectApi: () => ProjectApi;
|
|
33
5
|
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
34
6
|
declare const useLegacyAuth: () => {
|
|
35
7
|
setBaseUrl: (baseUrl: string) => void;
|
|
@@ -60,25 +32,26 @@ declare const useLegacyAuth: () => {
|
|
|
60
32
|
storage: import("@keboola/api-client").AsyncState<unknown>['state'];
|
|
61
33
|
};
|
|
62
34
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
type Props$1 = {
|
|
66
|
-
/** The flow-agnostic product API — the `api` half of a keboola client. */
|
|
67
|
-
api: KeboolaApi;
|
|
35
|
+
type Props$2 = {
|
|
36
|
+
api: ProjectApi;
|
|
68
37
|
children: ReactNode;
|
|
69
38
|
};
|
|
70
39
|
/**
|
|
71
|
-
* Provides
|
|
72
|
-
*
|
|
73
|
-
*
|
|
40
|
+
* Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.
|
|
41
|
+
*
|
|
42
|
+
* - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.
|
|
74
43
|
*/
|
|
75
|
-
declare const
|
|
76
|
-
|
|
77
|
-
|
|
44
|
+
declare const ProjectApiProvider: ({ api, children }: Props$2) => import("react").JSX.Element;
|
|
45
|
+
type Props$1 = {
|
|
46
|
+
api: StackApi;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
};
|
|
49
|
+
/** Provides one stack to `useStackApi()`. */
|
|
50
|
+
declare const StackApiProvider: ({ api, children }: Props$1) => import("react").JSX.Element;
|
|
51
|
+
/** The active stack's row as a re-rendering value; the default stack until something selects another. */
|
|
52
|
+
declare const useActiveStack: (session: BearerSession) => import("@keboola/api-client").StackEntry;
|
|
78
53
|
/** The session table as a re-rendering value — the binding every gate and panel reads it through. */
|
|
79
54
|
declare const useSessionState: (session: BearerSession) => import("@keboola/api-client").SessionState;
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/LegacyAuthProvider.d.ts
|
|
82
55
|
type Props = {
|
|
83
56
|
session: LegacySession;
|
|
84
57
|
/** Stack base URL. */
|
|
@@ -89,7 +62,7 @@ type Props = {
|
|
|
89
62
|
managementToken?: string;
|
|
90
63
|
/** Rendered until the storage build reaches ready. */
|
|
91
64
|
fallback?: ReactNode;
|
|
92
|
-
/** Rendered when the storage build fails —
|
|
65
|
+
/** Rendered when the storage build fails — the stack index could not be read. */
|
|
93
66
|
errorFallback?: ReactNode;
|
|
94
67
|
children: ReactNode;
|
|
95
68
|
};
|
|
@@ -100,6 +73,5 @@ type Props = {
|
|
|
100
73
|
* session via `useLegacyAuth()` and gates `children` behind readiness.
|
|
101
74
|
*/
|
|
102
75
|
declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, errorFallback, children }: Props) => import("react").JSX.Element;
|
|
103
|
-
|
|
104
|
-
export { ApiClientProvider, LegacyAuthProvider, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
|
|
76
|
+
export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
|
|
105
77
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import { createContext, use, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const MISSING_STACK_ERROR = "useStack must be used within <ApiClientProvider />";
|
|
3
|
+
// src/context.ts
|
|
4
|
+
const MISSING_STACK_API_ERROR = "useStackApi must be used within <StackApiProvider />";
|
|
5
|
+
const MISSING_PROJECT_API_ERROR = "useProjectApi must be used within <ProjectApiProvider />";
|
|
7
6
|
const MISSING_LEGACY_AUTH_ERROR = "useLegacyAuth must be used within <LegacyAuthProvider />";
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
/**
|
|
8
|
+
* One stack: what is there before a project — signing in, the account, the project list.
|
|
9
|
+
*
|
|
10
|
+
* - A `ProjectApiProvider` provides its project's stack here too.
|
|
11
|
+
*/
|
|
12
|
+
const StackApiContext = createContext(null);
|
|
13
|
+
const StackApiContextProvider = StackApiContext;
|
|
14
|
+
const ProjectApiContext = createContext(null);
|
|
15
|
+
const ProjectApiContextProvider = ProjectApiContext;
|
|
10
16
|
const LegacyAuthContext = createContext(null);
|
|
11
17
|
const LegacyAuthContextProvider = LegacyAuthContext;
|
|
12
|
-
const
|
|
13
|
-
const api = use(
|
|
14
|
-
if (api === null) throw new Error(
|
|
18
|
+
const useStackApi = () => {
|
|
19
|
+
const api = use(StackApiContext);
|
|
20
|
+
if (api === null) throw new Error(MISSING_STACK_API_ERROR);
|
|
15
21
|
return api;
|
|
16
22
|
};
|
|
17
|
-
const
|
|
18
|
-
const api = use(
|
|
19
|
-
if (api === null) throw new Error(
|
|
20
|
-
return api
|
|
21
|
-
};
|
|
22
|
-
const useStack = () => {
|
|
23
|
-
const api = use(ApiContext);
|
|
24
|
-
if (api === null) throw new Error(MISSING_STACK_ERROR);
|
|
25
|
-
return api.context.stack;
|
|
23
|
+
const useProjectApi = () => {
|
|
24
|
+
const api = use(ProjectApiContext);
|
|
25
|
+
if (api === null) throw new Error(MISSING_PROJECT_API_ERROR);
|
|
26
|
+
return api;
|
|
26
27
|
};
|
|
27
28
|
/** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
|
|
28
29
|
const useLegacyAuth = () => {
|
|
@@ -30,23 +31,32 @@ const useLegacyAuth = () => {
|
|
|
30
31
|
if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
|
|
31
32
|
return session;
|
|
32
33
|
};
|
|
33
|
-
|
|
34
|
-
//#region src/ApiClientProvider.tsx
|
|
34
|
+
// src/ProjectApiProvider.tsx
|
|
35
35
|
/**
|
|
36
|
-
* Provides
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.
|
|
37
|
+
*
|
|
38
|
+
* - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.
|
|
39
39
|
*/
|
|
40
|
-
const
|
|
40
|
+
const ProjectApiProvider = ({ api, children }) => /* @__PURE__ */ jsx(StackApiContextProvider, {
|
|
41
|
+
value: api,
|
|
42
|
+
children: /* @__PURE__ */ jsx(ProjectApiContextProvider, {
|
|
43
|
+
value: api,
|
|
44
|
+
children
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
// src/StackApiProvider.tsx
|
|
48
|
+
/** Provides one stack to `useStackApi()`. */
|
|
49
|
+
const StackApiProvider = ({ api, children }) => /* @__PURE__ */ jsx(StackApiContextProvider, {
|
|
41
50
|
value: api,
|
|
42
51
|
children
|
|
43
52
|
});
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
// src/useActiveStack.ts
|
|
54
|
+
/** The active stack's row as a re-rendering value; the default stack until something selects another. */
|
|
55
|
+
const useActiveStack = (session) => useSyncExternalStore(session.subscribe, session.stack.getActive);
|
|
56
|
+
// src/useSessionState.ts
|
|
46
57
|
/** The session table as a re-rendering value — the binding every gate and panel reads it through. */
|
|
47
58
|
const useSessionState = (session) => useSyncExternalStore(session.subscribe, session.getState);
|
|
48
|
-
|
|
49
|
-
//#region src/LegacyAuthProvider.tsx
|
|
59
|
+
// src/LegacyAuthProvider.tsx
|
|
50
60
|
/**
|
|
51
61
|
* Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
|
|
52
62
|
* ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
|
|
@@ -78,7 +88,6 @@ const LegacyAuthProvider = ({ session, baseUrl, storageToken, managementToken, f
|
|
|
78
88
|
})()
|
|
79
89
|
});
|
|
80
90
|
};
|
|
81
|
-
|
|
82
|
-
export { ApiClientProvider, LegacyAuthProvider, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
|
|
91
|
+
export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
|
|
83
92
|
|
|
84
93
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/ProjectApiProvider.tsx","../src/StackApiProvider.tsx","../src/useActiveStack.ts","../src/useSessionState.ts","../src/LegacyAuthProvider.tsx"],"sourcesContent":["import { createContext, use } from 'react';\n\nimport type { LegacySession, ProjectApi, StackApi } from '@keboola/api-client';\n\nconst MISSING_STACK_API_ERROR = 'useStackApi must be used within <StackApiProvider />';\nconst MISSING_PROJECT_API_ERROR = 'useProjectApi must be used within <ProjectApiProvider />';\nconst MISSING_LEGACY_AUTH_ERROR = 'useLegacyAuth must be used within <LegacyAuthProvider />';\n\n/**\n * One stack: what is there before a project — signing in, the account, the project list.\n *\n * - A `ProjectApiProvider` provides its project's stack here too.\n */\nconst StackApiContext = createContext<StackApi | null>(null);\nexport const StackApiContextProvider = StackApiContext;\n\n// One project, on its stack. Both flows produce the same `ProjectApi`, so consumers never branch on\n// flow.\nconst ProjectApiContext = createContext<ProjectApi | null>(null);\nexport const ProjectApiContextProvider = ProjectApiContext;\n\n// The legacy lifecycle session lives in its own context; the bearer flows read their table through\n// `useSessionState` instead.\nconst LegacyAuthContext = createContext<LegacySession | null>(null);\nexport const LegacyAuthContextProvider = LegacyAuthContext;\n\nexport const useStackApi = () => {\n const api = use(StackApiContext);\n if (api === null) throw new Error(MISSING_STACK_API_ERROR);\n return api;\n};\n\nexport const useProjectApi = () => {\n const api = use(ProjectApiContext);\n if (api === null) throw new Error(MISSING_PROJECT_API_ERROR);\n return api;\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 { ProjectApi } from '@keboola/api-client';\n\nimport { ProjectApiContextProvider, StackApiContextProvider } from './context';\n\ntype Props = {\n api: ProjectApi;\n children: ReactNode;\n};\n\n/**\n * Provides one project to `useProjectApi()`, and its stack to `useStackApi()`.\n *\n * - A pure pass-through: the auth lifecycle belongs to the session, or to `LegacyAuthProvider`.\n */\nexport const ProjectApiProvider = ({ api, children }: Props) => (\n <StackApiContextProvider value={api}>\n <ProjectApiContextProvider value={api}>{children}</ProjectApiContextProvider>\n </StackApiContextProvider>\n);\n","import type { ReactNode } from 'react';\n\nimport type { StackApi } from '@keboola/api-client';\n\nimport { StackApiContextProvider } from './context';\n\ntype Props = {\n api: StackApi;\n children: ReactNode;\n};\n\n/** Provides one stack to `useStackApi()`. */\nexport const StackApiProvider = ({ api, children }: Props) => (\n <StackApiContextProvider value={api}>{children}</StackApiContextProvider>\n);\n","import { useSyncExternalStore } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\n/** The active stack's row as a re-rendering value; the default stack until something selects another. */\nexport const useActiveStack = (session: BearerSession) =>\n useSyncExternalStore(session.subscribe, session.stack.getActive);\n","import { useSyncExternalStore } from 'react';\n\nimport type { BearerSession } from '@keboola/api-client';\n\n/** The session table as a re-rendering value — the binding every gate and panel reads it through. */\nexport const useSessionState = (session: BearerSession) =>\n useSyncExternalStore(session.subscribe, session.getState);\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 /** Rendered when the storage build fails — the stack index could not be read. */\n errorFallback?: 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 errorFallback = fallback,\n children,\n}: Props) => {\n // The build state, not just readiness: `errored` and `pending` render differently.\n const [storageState, setStorageState] = useState(() => session.debug().storage);\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((snapshot) => setStorageState(snapshot.storage));\n\n if (!startedRef.current) {\n startedRef.current = true;\n session.setBaseUrl(baseUrl);\n if (managementToken) session.authenticateManagement(managementToken);\n // Fire-and-forget: `subscribe` moves `storageState` once the build resolves. A failed build\n // resolves rather than rejects, so this catch only covers a synchronous throw.\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 // The only place a failed build is observable — nothing rejects, so without this it is silent.\n useEffect(() => {\n if (storageState === 'errored') {\n console.error('Keboola session bootstrap failed: the storage build errored.');\n }\n }, [storageState]);\n\n const content = (() => {\n if (storageState === 'ready') return children;\n if (storageState === 'errored') return errorFallback;\n return fallback;\n })();\n\n return <LegacyAuthContextProvider value={session}>{content}</LegacyAuthContextProvider>;\n};\n"],"mappings":";;;AAIA,MAAM,0BAA0B;AAChC,MAAM,4BAA4B;AAClC,MAAM,4BAA4B;;;;;;AAOlC,MAAM,kBAAkB,cAA+B,IAAI;AAC3D,MAAa,0BAA0B;AAIvC,MAAM,oBAAoB,cAAiC,IAAI;AAC/D,MAAa,4BAA4B;AAIzC,MAAM,oBAAoB,cAAoC,IAAI;AAClE,MAAa,4BAA4B;AAEzC,MAAa,oBAAoB;CAC/B,MAAM,MAAM,IAAI,eAAe;CAC/B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,uBAAuB;CACzD,OAAO;AACT;AAEA,MAAa,sBAAsB;CACjC,MAAM,MAAM,IAAI,iBAAiB;CACjC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC3D,OAAO;AACT;;AAGA,MAAa,sBAAsB;CACjC,MAAM,UAAU,IAAI,iBAAiB;CACrC,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC/D,OAAO;AACT;;;;;;;AC3BA,MAAa,sBAAsB,EAAE,KAAK,eACxC,oBAAC,yBAAD;CAAyB,OAAO;CAC9B,UAAA,oBAAC,2BAAD;EAA2B,OAAO;EAAM;CAAoC,CAAA;AACrD,CAAA;;;ACP3B,MAAa,oBAAoB,EAAE,KAAK,eACtC,oBAAC,yBAAD;CAAyB,OAAO;CAAM;AAAkC,CAAA;;;ACR1E,MAAa,kBAAkB,YAC7B,qBAAqB,QAAQ,WAAW,QAAQ,MAAM,SAAS;;;ACDjE,MAAa,mBAAmB,YAC9B,qBAAqB,QAAQ,WAAW,QAAQ,QAAQ;;;;;;;;ACsB1D,MAAa,sBAAsB,EACjC,SACA,SACA,cACA,iBACA,WAAW,MACX,gBAAgB,UAChB,eACW;CAEX,MAAM,CAAC,cAAc,mBAAmB,eAAe,QAAQ,MAAM,CAAC,CAAC,OAAO;CAE9E,MAAM,aAAa,OAAgB,KAAK;CAExC,gBAAgB;EACd,MAAM,cAAc,QAAQ,WAAW,aAAa,gBAAgB,SAAS,OAAO,CAAC;EAErF,IAAI,CAAC,WAAW,SAAS;GACvB,WAAW,UAAU;GACrB,QAAQ,WAAW,OAAO;GAC1B,IAAI,iBAAiB,QAAQ,uBAAuB,eAAe;GAGnE,QACG,yBAAyB,YAAY,CAAC,CACtC,OAAO,UAAU,QAAQ,MAAM,oCAAoC,KAAK,CAAC;EAC9E;EAEA,OAAO;CAGT,GAAG,CAAC,CAAC;CAGL,gBAAgB;EACd,IAAI,iBAAiB,WACnB,QAAQ,MAAM,8DAA8D;CAEhF,GAAG,CAAC,YAAY,CAAC;CAQjB,OAAO,oBAAC,2BAAD;EAA2B,OAAO;EANlB,iBAAA;GACrB,IAAI,iBAAiB,SAAS,OAAO;GACrC,IAAI,iBAAiB,WAAW,OAAO;GACvC,OAAO;EACT,EAAA,CAEyD;CAA6B,CAAA;AACxF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keboola/api-client-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "50.0.0",
|
|
4
4
|
"description": "React bindings for @keboola/api-client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -50,34 +50,34 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@keboola/api-client": "^
|
|
53
|
+
"@keboola/api-client": "^50.0.0",
|
|
54
54
|
"react": "^19.0.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
58
|
-
"@keboola/api-client": "^
|
|
59
|
-
"@keboola/api-fixtures": "^
|
|
60
|
-
"@keboola/eslint-plugin-design": "^0.
|
|
58
|
+
"@keboola/api-client": "^50.0.0",
|
|
59
|
+
"@keboola/api-fixtures": "^45.0.0",
|
|
60
|
+
"@keboola/eslint-plugin-design": "^0.6.0",
|
|
61
61
|
"@keboola/oxlint-config": "^10.2.0",
|
|
62
62
|
"@keboola/tsconfig": "^0.2.1",
|
|
63
|
-
"@testing-library/dom": "^10.4.
|
|
63
|
+
"@testing-library/dom": "^10.4.2",
|
|
64
64
|
"@testing-library/jest-dom": "^7.0.1",
|
|
65
65
|
"@testing-library/react": "^16.3.3",
|
|
66
66
|
"@types/node": "24.12.0",
|
|
67
67
|
"@types/react": "^19.3.0",
|
|
68
68
|
"@types/react-dom": "^19.3.0",
|
|
69
69
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
70
|
-
"@vitejs/plugin-react": "^6.1.
|
|
70
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
71
71
|
"jsdom": "^26.1.0",
|
|
72
72
|
"msw": "^2.15.0",
|
|
73
73
|
"oxfmt": "^0.66.0",
|
|
74
|
-
"oxlint": "^1.
|
|
75
|
-
"react": "^19.3.
|
|
76
|
-
"react-dom": "^19.3.
|
|
74
|
+
"oxlint": "^1.89.0",
|
|
75
|
+
"react": "^19.3.1",
|
|
76
|
+
"react-dom": "^19.3.1",
|
|
77
77
|
"tsdown": "^0.22.14",
|
|
78
78
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
79
|
-
"vite": "^8.3.
|
|
80
|
-
"vitest": "^5.0.
|
|
79
|
+
"vite": "^8.3.5",
|
|
80
|
+
"vitest": "^5.0.5"
|
|
81
81
|
},
|
|
82
82
|
"scripts": {
|
|
83
83
|
"type-check": "tsc",
|