@keboola/api-client-react 49.0.0 → 51.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 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 context that holds the typed `ApiClient` instance and the hooks for
5
- consumers to read it.
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
- useApiClient,
22
- useAuth,
19
+ ProjectApiProvider,
20
+ StackApiProvider,
21
+ useActiveStack,
23
22
  useLegacyAuth,
24
- useProject,
25
- useStack,
23
+ useProjectApi,
24
+ useSessionState,
25
+ useStackApi,
26
26
  } from '@keboola/api-client-react';
27
27
  ```
28
28
 
29
- A keboola client is `{ api, session }`. This package provides the two halves separately:
30
-
31
- - `ApiClientProvider` — takes the flow-agnostic `api` and provides it to descendants. A pure
32
- pass-through: no lifecycle, no fallback. `useApiClient()` returns that `api`.
33
- - `LegacyAuthProvider` — takes a **legacy** `session` (+ `baseUrl`, `storageToken`,
34
- optional `managementToken`). On mount it drives the session to ready
35
- (`setBaseUrl` → `authenticateManagement?` → `authenticateStorageAsync`) and renders `fallback`
36
- until `session.isStorageReady()`. Exposes the session via `useLegacyAuth()`.
37
- - `AuthProvider` — takes a **bearer** `session` (+ `baseUrl`, optional `onAccessTokenChange`). The
38
- bearer twin of `LegacyAuthProvider`. On mount it configures the stack and `restore()`s any
39
- persisted session, then runs one of two modes, discriminated by whether a credential is supplied:
40
- - **programmatic** — `credential` + `projectId` given; it drives itself to ready.
41
- - **interactive** — neither given; the session starts unscoped and only the consumer can scope it,
42
- so `anonymousFallback` is **required** by the type in this mode. Which slot actually renders at
43
- any moment is the table below — restoring and bootstrapping take `loadingFallback` first.
44
-
45
- It renders exactly one of four slots, by state:
46
-
47
- | State | Renders |
48
- | --------------------------------- | ------------------- |
49
- | scoped + storage build ready | `children` |
50
- | storage build failed | `errorFallback` |
51
- | restoring / bootstrapping | `loadingFallback` |
52
- | not yet scoped (interactive only) | `anonymousFallback` |
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
- <ApiClientProvider api={api}>{children}</ApiClientProvider>
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 value is typed `KeboolaApi | null` and the hooks throw on
101
- null — see [apps/AGENTS.md](../../apps/AGENTS.md) ("State Management"
102
- → "Tree-passing"). Don't soften this to optional types or `??` fallbacks;
103
- a missing provider is a programming error.
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
- - The auth provider drives the session to ready exactly once on mount. Changing
107
- its props after mount is a no-op by design — if you need a new auth context,
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
- `useApiClient()` is for React components only. A TanStack Router loader, a
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/app.tsx` — production `ApiClientProvider` mount
134
- - `apps/kbc-ui/src/scripts/modules/sandboxes/queries.ts` — `useApiClient`
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 — the auth provider
142
- (`AuthProvider` / `LegacyAuthProvider`) owns the drive-to-ready lifecycle.
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,35 @@
1
1
  # @keboola/api-client-react
2
2
 
3
+ ## 51.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies:
8
+ - @keboola/api-client@51.0.0
9
+
10
+ ## 50.0.0
11
+
12
+ ### Major Changes
13
+
14
+ - 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`.
15
+ - **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`.
16
+ - **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.
17
+ - **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.
18
+ - **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`.
19
+ - **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`).
20
+ - **Breaking:** `auth.getHeadersAsync(projectId)` requires the project, resolved on the active stack. The synchronous `auth.getHeaders` is gone.
21
+ - **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.
22
+ - The persisted session still writes `current.projectId: null` and an empty `projects` per stack, so an older build reading it does not sign out.
23
+ - **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.
24
+ - **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.
25
+ - **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.
26
+ - `api.storage` under the bearer flow is live from the sign-in on.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies:
31
+ - @keboola/api-client@50.0.0
32
+
3
33
  ## 49.0.0
4
34
 
5
35
  ### Minor 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
- 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`.
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 { ApiClientProvider, LegacyAuthProvider, useApiClient } from '@keboola/api-client-react';
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
- <ApiClientProvider api={api}>
32
+ <ProjectApiProvider api={api}>
33
33
  <Tables />
34
- </ApiClientProvider>
34
+ </ProjectApiProvider>
35
35
  </LegacyAuthProvider>
36
36
  );
37
37
 
38
38
  const Tables = () => {
39
- const api = useApiClient();
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
- - `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).
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
- //#region src/context.ts
5
- const MISSING_API_CLIENT_ERROR = "useApiClient must be used within <ApiClientProvider />";
6
- const MISSING_PROJECT_ERROR = "useProject must be used within <ApiClientProvider />";
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
- const ApiContext = (0, react.createContext)(null);
10
- const ApiProvider = ApiContext;
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 useApiClient = () => {
14
- const api = (0, react.use)(ApiContext);
15
- if (api === null) throw new Error(MISSING_API_CLIENT_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 useProject = () => {
19
- const api = (0, react.use)(ApiContext);
20
- if (api === null) throw new Error(MISSING_PROJECT_ERROR);
21
- return api.context.project;
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,27 +32,32 @@ const useLegacyAuth = () => {
31
32
  if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
32
33
  return session;
33
34
  };
34
- //#endregion
35
- //#region src/ApiClientProvider.tsx
35
+ // src/ProjectApiProvider.tsx
36
36
  /**
37
- * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
38
- * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
39
- * this provider once their session is ready.
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 ApiClientProvider = ({ api, children }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ApiProvider, {
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
- //#endregion
46
- //#region src/useActiveStack.ts
54
+ // src/useActiveStack.ts
47
55
  /** The active stack's row as a re-rendering value; the default stack until something selects another. */
48
56
  const useActiveStack = (session) => (0, react.useSyncExternalStore)(session.subscribe, session.stack.getActive);
49
- //#endregion
50
- //#region src/useSessionState.ts
57
+ // src/useSessionState.ts
51
58
  /** The session table as a re-rendering value — the binding every gate and panel reads it through. */
52
59
  const useSessionState = (session) => (0, react.useSyncExternalStore)(session.subscribe, session.getState);
53
- //#endregion
54
- //#region src/LegacyAuthProvider.tsx
60
+ // src/LegacyAuthProvider.tsx
55
61
  /**
56
62
  * Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
57
63
  * ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
@@ -83,14 +89,13 @@ const LegacyAuthProvider = ({ session, baseUrl, storageToken, managementToken, f
83
89
  })()
84
90
  });
85
91
  };
86
- //#endregion
87
- exports.ApiClientProvider = ApiClientProvider;
88
92
  exports.LegacyAuthProvider = LegacyAuthProvider;
93
+ exports.ProjectApiProvider = ProjectApiProvider;
94
+ exports.StackApiProvider = StackApiProvider;
89
95
  exports.useActiveStack = useActiveStack;
90
- exports.useApiClient = useApiClient;
91
96
  exports.useLegacyAuth = useLegacyAuth;
92
- exports.useProject = useProject;
97
+ exports.useProjectApi = useProjectApi;
93
98
  exports.useSessionState = useSessionState;
94
- exports.useStack = useStack;
99
+ exports.useStackApi = useStackApi;
95
100
 
96
101
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["createContext","use","useSyncExternalStore","useSyncExternalStore","useState","useRef"],"sources":["../src/context.ts","../src/ApiClientProvider.tsx","../src/useActiveStack.ts","../src/useSessionState.ts","../src/LegacyAuthProvider.tsx"],"sourcesContent":["import { createContext, use } from 'react';\n\nimport type { 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_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 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 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 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 { 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 — e.g. the SAPI token is expired or revoked. */\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,2BAA2B;AACjC,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAC5B,MAAM,4BAA4B;AAIlC,MAAM,cAAA,GAAaA,MAAAA,cAAAA,CAAiC,IAAI;AACxD,MAAa,cAAc;AAI3B,MAAM,qBAAA,GAAoBA,MAAAA,cAAAA,CAAoC,IAAI;AAClE,MAAa,4BAA4B;AAEzC,MAAa,qBAAqB;CAChC,MAAM,OAAA,GAAMC,MAAAA,IAAAA,CAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,wBAAwB;CAC1D,OAAO;AACT;AAEA,MAAa,mBAAmB;CAC9B,MAAM,OAAA,GAAMA,MAAAA,IAAAA,CAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,qBAAqB;CACvD,OAAO,IAAI,QAAQ;AACrB;AAEA,MAAa,iBAAiB;CAC5B,MAAM,OAAA,GAAMA,MAAAA,IAAAA,CAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,mBAAmB;CACrD,OAAO,IAAI,QAAQ;AACrB;;AAGA,MAAa,sBAAsB;CACjC,MAAM,WAAA,GAAUA,MAAAA,IAAAA,CAAI,iBAAiB;CACrC,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC/D,OAAO;AACT;;;;;;;;ACzBA,MAAa,qBAAqB,EAAE,KAAK,eACvC,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;CAAa,OAAO;CAAM;AAAsB,CAAA;;;;ACblD,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"}
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,36 +1,7 @@
1
- import { BearerSession, KeboolaApi, LegacySession } from "@keboola/api-client";
1
+ import { BearerSession, LegacySession, ProjectApi, StackApi } from "@keboola/api-client";
2
2
  import { ReactNode } from "react";
3
- //#region src/context.d.ts
4
- declare const useApiClient: () => KeboolaApi;
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
- canListSharingTargets: boolean;
10
- sapiToken: import("@keboola/api-client/storage/types").StorageToken;
11
- };
12
- declare const useStack: () => {
13
- stack: string;
14
- host: string;
15
- api: string;
16
- version: string;
17
- revision: string;
18
- documentation: string;
19
- urlTemplates: {
20
- orchestrationJob: string;
21
- };
22
- services: {
23
- id: string;
24
- url: string;
25
- }[];
26
- features: string[];
27
- components: import("@keboola/api-client/storage/types").StackComponent[];
28
- hasFeature: (...feature: string[]) => boolean;
29
- getServiceUrl: (serviceId: import("@keboola/api-client/constants").ServiceId) => string | null;
30
- hasService: (serviceId: import("@keboola/api-client/constants").ServiceId) => boolean;
31
- getComponent: (componentId: string) => import("@keboola/api-client/storage/types").StackComponent | null;
32
- hasComponent: (...componentId: string[]) => boolean;
33
- };
3
+ declare const useStackApi: () => StackApi;
4
+ declare const useProjectApi: () => ProjectApi;
34
5
  /** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
35
6
  declare const useLegacyAuth: () => {
36
7
  setBaseUrl: (baseUrl: string) => void;
@@ -61,29 +32,26 @@ declare const useLegacyAuth: () => {
61
32
  storage: import("@keboola/api-client").AsyncState<unknown>['state'];
62
33
  };
63
34
  };
64
- //#endregion
65
- //#region src/ApiClientProvider.d.ts
66
- type Props$1 = {
67
- /** The flow-agnostic product API — the `api` half of a keboola client. */
68
- api: KeboolaApi;
35
+ type Props$2 = {
36
+ api: ProjectApi;
69
37
  children: ReactNode;
70
38
  };
71
39
  /**
72
- * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
73
- * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
74
- * this provider once their session is ready.
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`.
75
43
  */
76
- declare const ApiClientProvider: ({ api, children }: Props$1) => import("react").JSX.Element;
77
- //#endregion
78
- //#region src/useActiveStack.d.ts
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;
79
51
  /** The active stack's row as a re-rendering value; the default stack until something selects another. */
80
52
  declare const useActiveStack: (session: BearerSession) => import("@keboola/api-client").StackEntry;
81
- //#endregion
82
- //#region src/useSessionState.d.ts
83
53
  /** The session table as a re-rendering value — the binding every gate and panel reads it through. */
84
54
  declare const useSessionState: (session: BearerSession) => import("@keboola/api-client").SessionState;
85
- //#endregion
86
- //#region src/LegacyAuthProvider.d.ts
87
55
  type Props = {
88
56
  session: LegacySession;
89
57
  /** Stack base URL. */
@@ -94,7 +62,7 @@ type Props = {
94
62
  managementToken?: string;
95
63
  /** Rendered until the storage build reaches ready. */
96
64
  fallback?: ReactNode;
97
- /** Rendered when the storage build fails — e.g. the SAPI token is expired or revoked. */
65
+ /** Rendered when the storage build fails — the stack index could not be read. */
98
66
  errorFallback?: ReactNode;
99
67
  children: ReactNode;
100
68
  };
@@ -105,6 +73,5 @@ type Props = {
105
73
  * session via `useLegacyAuth()` and gates `children` behind readiness.
106
74
  */
107
75
  declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, errorFallback, children }: Props) => import("react").JSX.Element;
108
- //#endregion
109
- export { ApiClientProvider, LegacyAuthProvider, useActiveStack, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
76
+ export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
110
77
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1,36 +1,7 @@
1
1
  import { ReactNode } from "react";
2
- import { BearerSession, KeboolaApi, LegacySession } from "@keboola/api-client";
3
- //#region src/context.d.ts
4
- declare const useApiClient: () => KeboolaApi;
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
- canListSharingTargets: boolean;
10
- sapiToken: import("@keboola/api-client/storage/types").StorageToken;
11
- };
12
- declare const useStack: () => {
13
- stack: string;
14
- host: string;
15
- api: string;
16
- version: string;
17
- revision: string;
18
- documentation: string;
19
- urlTemplates: {
20
- orchestrationJob: string;
21
- };
22
- services: {
23
- id: string;
24
- url: string;
25
- }[];
26
- features: string[];
27
- components: import("@keboola/api-client/storage/types").StackComponent[];
28
- hasFeature: (...feature: string[]) => boolean;
29
- getServiceUrl: (serviceId: import("@keboola/api-client/constants").ServiceId) => string | null;
30
- hasService: (serviceId: import("@keboola/api-client/constants").ServiceId) => boolean;
31
- getComponent: (componentId: string) => import("@keboola/api-client/storage/types").StackComponent | null;
32
- hasComponent: (...componentId: string[]) => boolean;
33
- };
2
+ import { BearerSession, LegacySession, ProjectApi, StackApi } from "@keboola/api-client";
3
+ declare const useStackApi: () => StackApi;
4
+ declare const useProjectApi: () => ProjectApi;
34
5
  /** The legacy (SAPI) lifecycle session. Throws outside `<LegacyAuthProvider />`. */
35
6
  declare const useLegacyAuth: () => {
36
7
  setBaseUrl: (baseUrl: string) => void;
@@ -61,29 +32,26 @@ declare const useLegacyAuth: () => {
61
32
  storage: import("@keboola/api-client").AsyncState<unknown>['state'];
62
33
  };
63
34
  };
64
- //#endregion
65
- //#region src/ApiClientProvider.d.ts
66
- type Props$1 = {
67
- /** The flow-agnostic product API — the `api` half of a keboola client. */
68
- api: KeboolaApi;
35
+ type Props$2 = {
36
+ api: ProjectApi;
69
37
  children: ReactNode;
70
38
  };
71
39
  /**
72
- * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
73
- * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
74
- * this provider once their session is ready.
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`.
75
43
  */
76
- declare const ApiClientProvider: ({ api, children }: Props$1) => import("react").JSX.Element;
77
- //#endregion
78
- //#region src/useActiveStack.d.ts
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;
79
51
  /** The active stack's row as a re-rendering value; the default stack until something selects another. */
80
52
  declare const useActiveStack: (session: BearerSession) => import("@keboola/api-client").StackEntry;
81
- //#endregion
82
- //#region src/useSessionState.d.ts
83
53
  /** The session table as a re-rendering value — the binding every gate and panel reads it through. */
84
54
  declare const useSessionState: (session: BearerSession) => import("@keboola/api-client").SessionState;
85
- //#endregion
86
- //#region src/LegacyAuthProvider.d.ts
87
55
  type Props = {
88
56
  session: LegacySession;
89
57
  /** Stack base URL. */
@@ -94,7 +62,7 @@ type Props = {
94
62
  managementToken?: string;
95
63
  /** Rendered until the storage build reaches ready. */
96
64
  fallback?: ReactNode;
97
- /** Rendered when the storage build fails — e.g. the SAPI token is expired or revoked. */
65
+ /** Rendered when the storage build fails — the stack index could not be read. */
98
66
  errorFallback?: ReactNode;
99
67
  children: ReactNode;
100
68
  };
@@ -105,6 +73,5 @@ type Props = {
105
73
  * session via `useLegacyAuth()` and gates `children` behind readiness.
106
74
  */
107
75
  declare const LegacyAuthProvider: ({ session, baseUrl, storageToken, managementToken, fallback, errorFallback, children }: Props) => import("react").JSX.Element;
108
- //#endregion
109
- export { ApiClientProvider, LegacyAuthProvider, useActiveStack, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
76
+ export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
110
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
- //#region src/context.ts
4
- const MISSING_API_CLIENT_ERROR = "useApiClient must be used within <ApiClientProvider />";
5
- const MISSING_PROJECT_ERROR = "useProject must be used within <ApiClientProvider />";
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
- const ApiContext = createContext(null);
9
- const ApiProvider = ApiContext;
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 useApiClient = () => {
13
- const api = use(ApiContext);
14
- if (api === null) throw new Error(MISSING_API_CLIENT_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 useProject = () => {
18
- const api = use(ApiContext);
19
- if (api === null) throw new Error(MISSING_PROJECT_ERROR);
20
- return api.context.project;
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,27 +31,32 @@ const useLegacyAuth = () => {
30
31
  if (session === null) throw new Error(MISSING_LEGACY_AUTH_ERROR);
31
32
  return session;
32
33
  };
33
- //#endregion
34
- //#region src/ApiClientProvider.tsx
34
+ // src/ProjectApiProvider.tsx
35
35
  /**
36
- * Provides the product API to `useApiClient()`. A pure pass-through: the auth lifecycle (driving
37
- * to ready, gating on a fallback) belongs to `AuthProvider` / `LegacyAuthProvider`, which mount
38
- * this provider once their session is ready.
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 ApiClientProvider = ({ api, children }) => /* @__PURE__ */ jsx(ApiProvider, {
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
- //#endregion
45
- //#region src/useActiveStack.ts
53
+ // src/useActiveStack.ts
46
54
  /** The active stack's row as a re-rendering value; the default stack until something selects another. */
47
55
  const useActiveStack = (session) => useSyncExternalStore(session.subscribe, session.stack.getActive);
48
- //#endregion
49
- //#region src/useSessionState.ts
56
+ // src/useSessionState.ts
50
57
  /** The session table as a re-rendering value — the binding every gate and panel reads it through. */
51
58
  const useSessionState = (session) => useSyncExternalStore(session.subscribe, session.getState);
52
- //#endregion
53
- //#region src/LegacyAuthProvider.tsx
59
+ // src/LegacyAuthProvider.tsx
54
60
  /**
55
61
  * Owns the legacy (SAPI) lifecycle: on mount it configures the stack and drives the session to
56
62
  * ready, rendering `fallback` until the storage build resolves. The backend-injected SAPI token is
@@ -82,7 +88,6 @@ const LegacyAuthProvider = ({ session, baseUrl, storageToken, managementToken, f
82
88
  })()
83
89
  });
84
90
  };
85
- //#endregion
86
- export { ApiClientProvider, LegacyAuthProvider, useActiveStack, useApiClient, useLegacyAuth, useProject, useSessionState, useStack };
91
+ export { LegacyAuthProvider, ProjectApiProvider, StackApiProvider, useActiveStack, useLegacyAuth, useProjectApi, useSessionState, useStackApi };
87
92
 
88
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/ApiClientProvider.tsx","../src/useActiveStack.ts","../src/useSessionState.ts","../src/LegacyAuthProvider.tsx"],"sourcesContent":["import { createContext, use } from 'react';\n\nimport type { 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_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 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 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 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 { 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 — e.g. the SAPI token is expired or revoked. */\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,2BAA2B;AACjC,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAC5B,MAAM,4BAA4B;AAIlC,MAAM,aAAa,cAAiC,IAAI;AACxD,MAAa,cAAc;AAI3B,MAAM,oBAAoB,cAAoC,IAAI;AAClE,MAAa,4BAA4B;AAEzC,MAAa,qBAAqB;CAChC,MAAM,MAAM,IAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,wBAAwB;CAC1D,OAAO;AACT;AAEA,MAAa,mBAAmB;CAC9B,MAAM,MAAM,IAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,qBAAqB;CACvD,OAAO,IAAI,QAAQ;AACrB;AAEA,MAAa,iBAAiB;CAC5B,MAAM,MAAM,IAAI,UAAU;CAC1B,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,mBAAmB;CACrD,OAAO,IAAI,QAAQ;AACrB;;AAGA,MAAa,sBAAsB;CACjC,MAAM,UAAU,IAAI,iBAAiB;CACrC,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,yBAAyB;CAC/D,OAAO;AACT;;;;;;;;ACzBA,MAAa,qBAAqB,EAAE,KAAK,eACvC,oBAAC,aAAD;CAAa,OAAO;CAAM;AAAsB,CAAA;;;;ACblD,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"}
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": "49.0.0",
3
+ "version": "51.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": "^49.0.0",
53
+ "@keboola/api-client": "^51.0.0",
54
54
  "react": "^19.0.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@arethetypeswrong/cli": "^0.18.2",
58
- "@keboola/api-client": "^49.0.0",
59
- "@keboola/api-fixtures": "^44.0.0",
60
- "@keboola/eslint-plugin-design": "^0.5.0",
58
+ "@keboola/api-client": "^51.0.0",
59
+ "@keboola/api-fixtures": "^46.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.1",
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.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.81.0",
75
- "react": "^19.3.0",
76
- "react-dom": "^19.3.0",
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.0",
80
- "vitest": "^5.0.1"
79
+ "vite": "^8.3.5",
80
+ "vitest": "^5.0.5"
81
81
  },
82
82
  "scripts": {
83
83
  "type-check": "tsc",