@defjs/react 0.0.1-beta.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +52 -54
  2. package/index.d.ts +24 -15
  3. package/index.js +21 -14
  4. package/package.json +3 -2
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @defjs/react
2
2
 
3
- Thin React adapter for `@defjs/core`. It provides an existing Defjs client through React Context.
3
+ Thin React plugin for `@defjs/core`: it provides an application-owned client through React Context. It adds no query layer, cache, Suspense integration, or state management, and never creates, replaces, or disposes a client.
4
4
 
5
- Supports React 18+.
5
+ Keeping SDK definitions in their own package means the SDK needs no React dependency. Requires React 18 or later; the published entry is marked `'use client'`.
6
6
 
7
7
  ## Install
8
8
 
@@ -10,95 +10,93 @@ Supports React 18+.
10
10
  bun add @defjs/core @defjs/react react
11
11
  ```
12
12
 
13
- The package is ESM and requires `@defjs/core` as a peer in the `^0.4.0` range. Its tarball retains this `README.md` and the repository `LICENSE`; repository-wide guides and examples remain outside the package.
13
+ The package is ESM and requires `@defjs/core` as a peer in the `^0.5.0` range.
14
14
 
15
- ## What this package does
16
-
17
- `ClientProvider` exposes the exact `Client` instance supplied by the application. `useClient()` reads the nearest provided instance.
18
-
19
- Client creation and configuration stay in `@defjs/core`. The adapter does not create, cache, replace, dispose, abort, or close anything on behalf of the application, and it does not add a query layer, Suspense integration, GraphQL client, or application state management.
20
-
21
- ## Quick Start
22
-
23
- Define requests in a shared module:
15
+ ## Quick start
24
16
 
25
17
  ```tsx
26
- // api.ts
27
- import { defineRequest, struct } from '@defjs/core'
18
+ // api.ts — reusable definition
19
+ import { defineRequest, struct } from "@defjs/core";
28
20
 
29
21
  export const getUser = defineRequest({
30
- method: 'GET',
31
- path: '/users/:id',
32
- input: struct.object({
33
- path: struct.object({ id: struct.number() }),
34
- }),
22
+ method: "GET",
23
+ path: "/users/:id",
24
+ input: struct.object({ id: struct.number() }),
25
+ builder: (input) => ({ path: input }),
35
26
  output: struct.object({ id: struct.number(), name: struct.string() }),
36
- })
27
+ });
37
28
  ```
38
29
 
39
- Create and own the client at the application boundary, then provide it:
40
-
41
30
  ```tsx
42
31
  // app.tsx
43
- import { createClient, withEndpoint } from '@defjs/core'
44
- import { ClientProvider } from '@defjs/react'
45
- import { UserProfile } from './user-profile'
32
+ import { createClient, withEndpoint } from "@defjs/core";
33
+ import { ClientProvider } from "@defjs/react";
34
+ import { UserProfile } from "./user-profile";
46
35
 
47
- const client = createClient(withEndpoint('https://api.example.com'))
36
+ const client = createClient(withEndpoint("https://api.example.com"));
48
37
 
49
38
  export function App() {
50
39
  return (
51
40
  <ClientProvider client={client}>
52
41
  <UserProfile id={1} />
53
42
  </ClientProvider>
54
- )
43
+ );
55
44
  }
56
45
  ```
57
46
 
58
- Read the client inside descendants and own request cleanup where the work starts:
47
+ Read the client with `useClient()` and own request cleanup where the work starts:
59
48
 
60
49
  ```tsx
61
- import { useEffect, useState } from 'react'
62
- import { useClient } from '@defjs/react'
63
- import { getUser } from './api'
50
+ import { useEffect, useState } from "react";
51
+ import { useClient } from "@defjs/react";
52
+ import { getUser } from "./api";
64
53
 
65
54
  export function UserProfile({ id }: { id: number }) {
66
- const client = useClient()
67
- const [name, setName] = useState('loading...')
55
+ const client = useClient();
56
+ const [name, setName] = useState("loading...");
68
57
 
69
58
  useEffect(() => {
70
- const abort = new AbortController()
71
-
72
- void client.execute(getUser({ path: { id } }), { signal: abort.signal }).then(([error, user]) => {
73
- if (!abort.signal.aborted) setName(error ? error.message : user.name)
74
- })
75
-
76
- return () => abort.abort()
77
- }, [client, id])
78
-
79
- return <div>{name}</div>
59
+ const abort = new AbortController();
60
+
61
+ void getUser(client({ abort: abort.signal }), { id }).then(
62
+ ([error, user]) => {
63
+ if (abort.signal.aborted) return;
64
+ if (error) {
65
+ setName(error.message);
66
+ return;
67
+ }
68
+ setName(user.name);
69
+ },
70
+ );
71
+
72
+ return () => abort.abort();
73
+ }, [client, id]);
74
+
75
+ return <div>{name}</div>;
80
76
  }
81
77
  ```
82
78
 
83
- Nested providers follow normal React Context rules: descendants receive the nearest provider's exact client. Supplying a different `client` prop changes the provided value; the adapter does not invent a remount or caching policy.
84
-
85
79
  ## API
86
80
 
87
- ### `<ClientProvider client: Client>`
81
+ ### `<ClientProvider client={client}>`
88
82
 
89
- Provides the supplied client to descendant components. `children` is optional.
83
+ Provides the exact supplied `Client` to descendants. Nested providers follow normal React Context rules.
90
84
 
91
- ### `useClient(): Client`
85
+ ### `useClient(overrideClient?): Client`
92
86
 
93
- Returns the client from the nearest `ClientProvider`. Throws if called outside a provider.
87
+ Returns `overrideClient` when supplied, otherwise the nearest provided client. Throws only when neither source supplies one. Call it at the top level of a component or Hook — never as `explicitClient ?? useClient()`, which conditionally calls a Hook. The override does not write Context.
94
88
 
95
89
  ## Notes
96
90
 
97
- - The published entry is marked with `'use client'`, so React Server Component consumers treat this adapter as a client boundary.
98
- - Create request-specific clients at the application boundary for SSR data that contains credentials, cookies, or tenant state.
99
- - The owner that creates a client also owns all requests, SSE streams, and WebSocket sessions started through it. Provider unmount does not clean them up automatically.
100
- - Import `createClient`, `withEndpoint`, `withInterceptors`, and all other client options from `@defjs/core`.
91
+ - `Client` is a function. `useClient()` returns that same function. Do not pass it to `useState` or a state setter: React calls a function argument as a lazy initializer or an updater and will invoke the client.
92
+ - Create request-specific clients (SSR data with credentials, cookies, or tenant state) at the application boundary.
93
+ - The owner that creates a client also owns its requests, SSE streams, and WebSocket sessions. Provider unmount does not clean them up.
94
+ - Import `createClient`, `withEndpoint`, `withInterceptors`, and other client options from `@defjs/core`.
95
+
96
+ ## Documentation
97
+
98
+ - [React API](https://defjs.lyz.cloud/api/react) · [React guide](https://defjs.lyz.cloud/docs/integrations/react)
101
99
 
102
100
  ## License
103
101
 
104
- MIT License.
102
+ MIT.
package/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
 
2
2
  import { ReactNode } from "react";
3
3
  import { Client } from "@defjs/core";
4
- //#region src/core.d.ts
5
- /** Props for {@link ClientProvider}. */
4
+ /**
5
+ * Props of {@link ClientProvider}: the required `client` and optional `children`.
6
+ *
7
+ * @group react
8
+ */
6
9
  interface ClientProviderProps {
7
10
  /** Defjs client owned by the application. */
8
11
  client: Client;
@@ -12,30 +15,36 @@ interface ClientProviderProps {
12
15
  /**
13
16
  * Provide a Defjs client to React descendants via context.
14
17
  *
15
- * Does not create, cache, or dispose the client — pass an existing instance.
18
+ * Pass an existing client from `createClient` as `client`; descendants read it with `useClient`. The component does not create, cache, or dispose the client. A nested `ClientProvider` overrides the client for its own subtree, and a different `client` prop changes the context value for descendants.
16
19
  *
17
20
  * @param props - `client` and optional `children`.
18
21
  * @returns A context provider element.
19
22
  *
20
- * @example
21
- * ```tsx
22
- * <ClientProvider client={client}>
23
- * <App />
24
- * </ClientProvider>
25
- * ```
23
+ * @group react
24
+ * @see {@link Client}
25
+ * @see {@link ClientProviderProps}
26
+ * @exampleFile examples/react/client-provider.tsx#example
26
27
  */
27
- export declare function ClientProvider({ client, children }: ClientProviderProps): import("react").FunctionComponentElement<import("react").ProviderProps<Client | null>>;
28
+ export declare function ClientProvider({ client, children }: ClientProviderProps): React.FunctionComponentElement<React.ProviderProps<Client | null>>;
28
29
  /**
29
- * Read the Defjs client from the nearest {@link ClientProvider}.
30
+ * React Hook that returns an explicit Defjs client or the nearest {@link ClientProvider}.
31
+ *
32
+ * `useClient(overrideClient)` prefers the exact explicit client; omitted or `undefined` reads the nearest `ClientProvider`. An `Error` is thrown only when neither source supplies a client. Zero-argument `useClient()` calls remain valid.
33
+ *
34
+ * Always call `useClient(explicitClient)` at the top level of a component or custom Hook, even with an explicit client; not in an ordinary function or event callback. Do not write `explicitClient ?? useClient()`, which conditionally calls a Hook. The override does not write Context or create a provider, and the Hook still subscribes to Context.
35
+ *
36
+ * The returned value is that same function. `Client` is callable, so do not pass it to `useState` or a state setter: React calls a function argument as a lazy initializer or an updater.
30
37
  *
31
- * @returns The provided `Client`.
32
- * @throws If called outside a `ClientProvider`.
38
+ * @param overrideClient - Client to use before context; `undefined` uses the provider.
39
+ * @returns The selected client function.
40
+ * @throws If neither an explicit client nor a `ClientProvider` is available.
33
41
  *
34
42
  * @example
35
43
  * ```tsx
36
44
  * const client = useClient()
37
45
  * ```
46
+ * @group react
47
+ * @see {@link Client}
38
48
  */
39
- export declare function useClient(): Client;
40
- //#endregion
49
+ export declare function useClient(overrideClient?: Client): Client;
41
50
  export type { ClientProviderProps };
package/index.js CHANGED
@@ -1,40 +1,47 @@
1
1
  'use client';
2
2
  import { createContext, createElement, useContext } from "react";
3
- //#region src/core.ts
3
+ // src/core.ts
4
4
  const HttpClientContext = createContext(null);
5
5
  /**
6
6
  * Provide a Defjs client to React descendants via context.
7
7
  *
8
- * Does not create, cache, or dispose the client — pass an existing instance.
8
+ * Pass an existing client from `createClient` as `client`; descendants read it with `useClient`. The component does not create, cache, or dispose the client. A nested `ClientProvider` overrides the client for its own subtree, and a different `client` prop changes the context value for descendants.
9
9
  *
10
10
  * @param props - `client` and optional `children`.
11
11
  * @returns A context provider element.
12
12
  *
13
- * @example
14
- * ```tsx
15
- * <ClientProvider client={client}>
16
- * <App />
17
- * </ClientProvider>
18
- * ```
13
+ * @group react
14
+ * @see {@link Client}
15
+ * @see {@link ClientProviderProps}
16
+ * @exampleFile examples/react/client-provider.tsx#example
19
17
  */
20
18
  function ClientProvider({ client, children }) {
21
19
  return createElement(HttpClientContext.Provider, { value: client }, children);
22
20
  }
23
21
  /**
24
- * Read the Defjs client from the nearest {@link ClientProvider}.
22
+ * React Hook that returns an explicit Defjs client or the nearest {@link ClientProvider}.
23
+ *
24
+ * `useClient(overrideClient)` prefers the exact explicit client; omitted or `undefined` reads the nearest `ClientProvider`. An `Error` is thrown only when neither source supplies a client. Zero-argument `useClient()` calls remain valid.
25
+ *
26
+ * Always call `useClient(explicitClient)` at the top level of a component or custom Hook, even with an explicit client; not in an ordinary function or event callback. Do not write `explicitClient ?? useClient()`, which conditionally calls a Hook. The override does not write Context or create a provider, and the Hook still subscribes to Context.
27
+ *
28
+ * The returned value is that same function. `Client` is callable, so do not pass it to `useState` or a state setter: React calls a function argument as a lazy initializer or an updater.
25
29
  *
26
- * @returns The provided `Client`.
27
- * @throws If called outside a `ClientProvider`.
30
+ * @param overrideClient - Client to use before context; `undefined` uses the provider.
31
+ * @returns The selected client function.
32
+ * @throws If neither an explicit client nor a `ClientProvider` is available.
28
33
  *
29
34
  * @example
30
35
  * ```tsx
31
36
  * const client = useClient()
32
37
  * ```
38
+ * @group react
39
+ * @see {@link Client}
33
40
  */
34
- function useClient() {
35
- const client = useContext(HttpClientContext);
41
+ function useClient(overrideClient) {
42
+ const providedClient = useContext(HttpClientContext);
43
+ const client = overrideClient ?? providedClient;
36
44
  if (!client) throw new Error("No Defjs client provided. Did you forget to wrap your app in <ClientProvider>?");
37
45
  return client;
38
46
  }
39
- //#endregion
40
47
  export { ClientProvider, useClient };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@defjs/react",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.2",
4
+ "description": "Provide an existing Defjs client to a React tree.",
4
5
  "bugs": {
5
6
  "url": "https://github.com/defjs/defjs/issues"
6
7
  },
@@ -13,7 +14,7 @@
13
14
  "main": "./index.js",
14
15
  "types": "./index.d.ts",
15
16
  "peerDependencies": {
16
- "@defjs/core": "0.4.0-beta.1",
17
+ "@defjs/core": "^0.5.0",
17
18
  "react": ">=18.0.0"
18
19
  },
19
20
  "module": "./index.js",