@lyeve-labs/client-react 0.2.1
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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/index.cjs +101 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LyEve Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# @lyeve/cms-client-react
|
|
2
|
+
|
|
3
|
+
React hooks for the LyEve Core. Typed, reactive data fetching built on
|
|
4
|
+
`@lyeve/cms-client`.
|
|
5
|
+
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://react.dev)
|
|
8
|
+
[](https://www.typescriptlang.org)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @lyeve/cms-client @lyeve/cms-client-react
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { CmsProvider, useQuery, useMutation } from "@lyeve/cms-client-react";
|
|
16
|
+
import { getSchemas, createSchema } from "@lyeve/cms-client-rest";
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<CmsProvider config={{ baseUrl: "https://cms.example.com" }}>
|
|
21
|
+
<SchemaManager />
|
|
22
|
+
</CmsProvider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function SchemaManager() {
|
|
27
|
+
const { data, loading } = useQuery((client) => getSchemas(client));
|
|
28
|
+
const [create, { loading: creating }] = useMutation(
|
|
29
|
+
(client, vars: { name: string }) => createSchema(client, vars),
|
|
30
|
+
);
|
|
31
|
+
// ...
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
One provider at the root, typed hooks everywhere else. No boilerplate.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What's in the box
|
|
40
|
+
|
|
41
|
+
- **CmsProvider:** context provider that wraps your component tree with CMS client
|
|
42
|
+
configuration. `getHeaders` is called on every request so auth tokens stay fresh.
|
|
43
|
+
- **useQuery:** reactive data fetching hook. Runs on mount, returns `{ data, error,
|
|
44
|
+
loading, refetch }`. Preserves existing data on fetch errors to prevent UI flash.
|
|
45
|
+
- **useMutation:** mutation hook returning `[trigger, state]`. Loading/error/data
|
|
46
|
+
tracked per invocation.
|
|
47
|
+
- **Stale-closure safe:** client re-creates when `baseUrl` or `getHeaders` change,
|
|
48
|
+
so hooks always see the latest config.
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
- **Node 20** or newer
|
|
53
|
+
- **React 18** or newer
|
|
54
|
+
- **[@lyeve/cms-client](https://www.npmjs.com/package/@lyeve/cms-client)** `>=0.1.0`
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm add @lyeve/cms-client @lyeve/cms-client-react
|
|
60
|
+
# or npm install @lyeve/cms-client @lyeve/cms-client-react
|
|
61
|
+
# or yarn add @lyeve/cms-client @lyeve/cms-client-react
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Use
|
|
65
|
+
|
|
66
|
+
### Provider
|
|
67
|
+
|
|
68
|
+
Wrap your app once at the root:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { CmsProvider } from "@lyeve/cms-client-react";
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
return (
|
|
75
|
+
<CmsProvider
|
|
76
|
+
config={{
|
|
77
|
+
baseUrl: "https://cms.example.com",
|
|
78
|
+
getHeaders: () => ({ Authorization: `Bearer ${getToken()}` }),
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
<YourRoutes />
|
|
82
|
+
</CmsProvider>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### useQuery
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { useQuery } from "@lyeve/cms-client-react";
|
|
91
|
+
import { getSchemas } from "@lyeve/cms-client-rest";
|
|
92
|
+
|
|
93
|
+
function SchemaList() {
|
|
94
|
+
const { data, error, loading, refetch } = useQuery((client) =>
|
|
95
|
+
getSchemas(client),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (loading) return <div>Loading...</div>;
|
|
99
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<ul>
|
|
103
|
+
{data?.map((s) => (
|
|
104
|
+
<li key={s.id}>{s.name}</li>
|
|
105
|
+
))}
|
|
106
|
+
</ul>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### useMutation
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { useMutation } from "@lyeve/cms-client-react";
|
|
115
|
+
import { createSchema } from "@lyeve/cms-client-rest";
|
|
116
|
+
|
|
117
|
+
function CreateForm() {
|
|
118
|
+
const [create, { loading, error }] = useMutation(
|
|
119
|
+
(client, vars: { name: string }) => createSchema(client, vars),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
async function handleSubmit(e: React.FormEvent) {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
await create({ name: "articles" });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<form onSubmit={handleSubmit}>
|
|
129
|
+
<button type="submit" disabled={loading}>
|
|
130
|
+
{loading ? "Creating..." : "Create"}
|
|
131
|
+
</button>
|
|
132
|
+
{error && <p>{error.message}</p>}
|
|
133
|
+
</form>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## API
|
|
139
|
+
|
|
140
|
+
### CmsProvider
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
interface CmsConfig {
|
|
144
|
+
baseUrl?: string;
|
|
145
|
+
getHeaders?: () => Record<string, string>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
<CmsProvider config={config}>{children}</CmsProvider>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### useQuery
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
function useQuery<T>(
|
|
155
|
+
fetcher: (client: HttpClient) => Promise<T>,
|
|
156
|
+
deps?: unknown[],
|
|
157
|
+
): {
|
|
158
|
+
data: T | null;
|
|
159
|
+
error: Error | null;
|
|
160
|
+
loading: boolean;
|
|
161
|
+
refetch: () => void;
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Runs `fetcher` on mount and whenever `deps` change. Returns `refetch` for manual
|
|
166
|
+
re-execution.
|
|
167
|
+
|
|
168
|
+
### useMutation
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
function useMutation<T, V>(
|
|
172
|
+
mutator: (client: HttpClient, vars: V) => Promise<T>,
|
|
173
|
+
): [
|
|
174
|
+
(vars: V) => Promise<T>,
|
|
175
|
+
{ data: T | null; error: Error | null; loading: boolean },
|
|
176
|
+
];
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Returns a trigger function and the current state.
|
|
180
|
+
|
|
181
|
+
## Local development
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pnpm install # install dependencies
|
|
185
|
+
pnpm test # run unit tests
|
|
186
|
+
pnpm check # type-check
|
|
187
|
+
pnpm build # tsup + publint -> dist/
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Project layout
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
src/
|
|
194
|
+
index.tsx # CmsProvider, useQuery, useMutation
|
|
195
|
+
tests/ # vitest test suite
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Versioning
|
|
199
|
+
|
|
200
|
+
`@lyeve/cms-client-react` follows [SemVer](https://semver.org). While under `1.0`,
|
|
201
|
+
breaking changes bump the **minor** version; additive changes bump the **patch**.
|
|
202
|
+
Every release is logged in [`CHANGELOG.md`](CHANGELOG.md).
|
|
203
|
+
|
|
204
|
+
## Contributing
|
|
205
|
+
|
|
206
|
+
Bug reports and feature requests are welcome. See
|
|
207
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md) for the development setup and conventions.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT. See [`LICENSE`](LICENSE).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.tsx
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
CmsProvider: () => CmsProvider,
|
|
24
|
+
useMutation: () => useMutation,
|
|
25
|
+
useQuery: () => useQuery
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
|
+
var import_cms_client = require("@lyeve/cms-client");
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
+
var CmsContext = (0, import_react.createContext)(null);
|
|
32
|
+
function CmsProvider({
|
|
33
|
+
config,
|
|
34
|
+
children
|
|
35
|
+
}) {
|
|
36
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CmsContext.Provider, { value: config, children });
|
|
37
|
+
}
|
|
38
|
+
function useClient() {
|
|
39
|
+
const config = (0, import_react.useContext)(CmsContext);
|
|
40
|
+
if (!config) {
|
|
41
|
+
throw new Error("CmsProvider must wrap your component tree");
|
|
42
|
+
}
|
|
43
|
+
return (0, import_react.useMemo)(() => {
|
|
44
|
+
const base = config.baseUrl ?? "";
|
|
45
|
+
return (0, import_cms_client.createClient)((url, init) => {
|
|
46
|
+
const fullUrl = typeof url === "string" ? `${base}${url}` : url;
|
|
47
|
+
return fetch(fullUrl, {
|
|
48
|
+
...init,
|
|
49
|
+
headers: { ...init?.headers, ...config.getHeaders?.() }
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}, [config.baseUrl, config.getHeaders]);
|
|
53
|
+
}
|
|
54
|
+
function useQuery(fetcher, deps = []) {
|
|
55
|
+
const client = useClient();
|
|
56
|
+
const [state, setState] = (0, import_react.useState)({
|
|
57
|
+
data: null,
|
|
58
|
+
error: null,
|
|
59
|
+
loading: true
|
|
60
|
+
});
|
|
61
|
+
const run = (0, import_react.useCallback)(() => {
|
|
62
|
+
setState((s) => ({ ...s, loading: true }));
|
|
63
|
+
fetcher(client).then((data) => setState({ data, error: null, loading: false })).catch(
|
|
64
|
+
(error) => setState((s) => ({ ...s, error, loading: false }))
|
|
65
|
+
);
|
|
66
|
+
}, [client, ...deps]);
|
|
67
|
+
(0, import_react.useEffect)(() => {
|
|
68
|
+
run();
|
|
69
|
+
}, [run]);
|
|
70
|
+
return { ...state, refetch: run };
|
|
71
|
+
}
|
|
72
|
+
function useMutation(mutator) {
|
|
73
|
+
const client = useClient();
|
|
74
|
+
const [state, setState] = (0, import_react.useState)({
|
|
75
|
+
data: null,
|
|
76
|
+
error: null,
|
|
77
|
+
loading: false
|
|
78
|
+
});
|
|
79
|
+
const run = (0, import_react.useCallback)(
|
|
80
|
+
async (vars) => {
|
|
81
|
+
setState((s) => ({ ...s, loading: true }));
|
|
82
|
+
try {
|
|
83
|
+
const data = await mutator(client, vars);
|
|
84
|
+
setState({ data, error: null, loading: false });
|
|
85
|
+
return data;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
setState({ data: null, error, loading: false });
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[client]
|
|
92
|
+
);
|
|
93
|
+
return [run, state];
|
|
94
|
+
}
|
|
95
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
96
|
+
0 && (module.exports = {
|
|
97
|
+
CmsProvider,
|
|
98
|
+
useMutation,
|
|
99
|
+
useQuery
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/**\n * LyEve CMS React hooks - typed, reactive data fetching.\n *\n * Thin wrapper around {@link @lyeve/cms-client} that makes the client\n * available via context and provides `useQuery` / `useMutation` hooks.\n *\n * @example\n * ```tsx\n * import { CmsProvider, useQuery } from '@lyeve/cms-client-react';\n * import { getSchemas } from '@lyeve/cms-client-rest';\n *\n * function App() {\n * return (\n * <CmsProvider config={{ baseUrl: 'https://cms.example.com', getHeaders: () => ({ Authorization: `Bearer ${token}` }) }}>\n * <SchemaManager />\n * </CmsProvider>\n * );\n * }\n *\n * function SchemaManager() {\n * const { data: schemas, loading } = useQuery((client) => getSchemas(client));\n * // ...\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createClient, type HttpClient } from '@lyeve/cms-client';\nimport {\n createContext,\n useContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\n// Provider\n\nexport interface CmsConfig {\n /** Base URL prepended to every request path. */\n baseUrl?: string;\n /**\n * Callback returning headers added to every request.\n * Called on every request so auth tokens can be refreshed without\n * recreating the provider.\n */\n getHeaders?: () => Record<string, string>;\n}\n\nconst CmsContext = createContext<CmsConfig | null>(null);\n\n/**\n * Context provider that makes the CMS client available to all descendant\n * hooks.\n *\n * @example\n * ```tsx\n * <CmsProvider config={{ baseUrl: 'https://cms.example.com' }}>\n * <App />\n * </CmsProvider>\n * ```\n */\nexport function CmsProvider({\n config,\n children,\n}: {\n config: CmsConfig;\n children: ReactNode;\n}) {\n return <CmsContext.Provider value={config}>{children}</CmsContext.Provider>;\n}\n\n/**\n * Returns a memoized HttpClient instance configured from the nearest\n * {@link CmsProvider}.\n */\nfunction useClient(): HttpClient {\n const config = useContext(CmsContext);\n if (!config) {\n throw new Error('CmsProvider must wrap your component tree');\n }\n // Re-create when config changes so getHeaders/auth tokens stay current.\n return useMemo(() => {\n const base = config.baseUrl ?? '';\n return createClient((url, init) => {\n const fullUrl = typeof url === 'string' ? `${base}${url}` : url;\n return fetch(fullUrl, {\n ...init,\n headers: { ...init?.headers, ...config.getHeaders?.() },\n });\n });\n }, [config.baseUrl, config.getHeaders]);\n}\n\n// Hooks\n\nexport interface AsyncState<T> {\n data: T | null;\n error: Error | null;\n loading: boolean;\n}\n\n/**\n * Reactive query hook. Runs `fetcher` on mount and whenever `deps` change.\n *\n * @param fetcher - Function that receives the configured HttpClient and returns a promise.\n * @param deps - Optional dependency array controlling when to refetch (default: `[]`).\n *\n * @example\n * ```ts\n * const { data, error, loading, refetch } = useQuery(\n * (client) => getSchemas(client),\n * );\n * ```\n */\nexport function useQuery<T>(\n fetcher: (client: HttpClient) => Promise<T>,\n deps: unknown[] = [],\n): AsyncState<T> & { refetch: () => void } {\n const client = useClient();\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n error: null,\n loading: true,\n });\n\n const run = useCallback(() => {\n setState((s) => ({ ...s, loading: true }));\n\n fetcher(client)\n .then((data) => setState({ data, error: null, loading: false }))\n .catch((error) =>\n setState((s) => ({ ...s, error: error as Error, loading: false })),\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, ...deps]);\n\n useEffect(() => {\n run();\n }, [run]);\n\n return { ...state, refetch: run };\n}\n\n/**\n * Mutation hook. Returns a trigger function and the current state.\n *\n * @param mutator - Function that receives the configured HttpClient and variables.\n *\n * @example\n * ```ts\n * const [create, { loading, error }] = useMutation(\n * (client, vars: { title: string }) => createArticle(client, vars),\n * );\n * // await create({ title: 'Hello' })\n * ```\n */\nexport function useMutation<T, V>(\n mutator: (client: HttpClient, vars: V) => Promise<T>,\n): [(vars: V) => Promise<T>, AsyncState<T>] {\n const client = useClient();\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n error: null,\n loading: false,\n });\n\n const run = useCallback(\n async (vars: V) => {\n setState((s) => ({ ...s, loading: true }));\n try {\n const data = await mutator(client, vars);\n setState({ data, error: null, loading: false });\n return data;\n } catch (error) {\n setState({ data: null, error: error as Error, loading: false });\n throw error;\n }\n },\n [client],\n );\n\n return [run, state];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BA,wBAA8C;AAC9C,mBAQO;AAmCE;AApBT,IAAM,iBAAa,4BAAgC,IAAI;AAahD,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AACF,GAGG;AACD,SAAO,4CAAC,WAAW,UAAX,EAAoB,OAAO,QAAS,UAAS;AACvD;AAMA,SAAS,YAAwB;AAC/B,QAAM,aAAS,yBAAW,UAAU;AACpC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AAEA,aAAO,sBAAQ,MAAM;AACnB,UAAM,OAAO,OAAO,WAAW;AAC/B,eAAO,gCAAa,CAAC,KAAK,SAAS;AACjC,YAAM,UAAU,OAAO,QAAQ,WAAW,GAAG,IAAI,GAAG,GAAG,KAAK;AAC5D,aAAO,MAAM,SAAS;AAAA,QACpB,GAAG;AAAA,QACH,SAAS,EAAE,GAAG,MAAM,SAAS,GAAG,OAAO,aAAa,EAAE;AAAA,MACxD,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,SAAS,OAAO,UAAU,CAAC;AACxC;AAuBO,SAAS,SACd,SACA,OAAkB,CAAC,GACsB;AACzC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB;AAAA,IAChD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAED,QAAM,UAAM,0BAAY,MAAM;AAC5B,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AAEzC,YAAQ,MAAM,EACX,KAAK,CAAC,SAAS,SAAS,EAAE,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,EAC9D;AAAA,MAAM,CAAC,UACN,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAuB,SAAS,MAAM,EAAE;AAAA,IACnE;AAAA,EAEJ,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;AAEpB,8BAAU,MAAM;AACd,QAAI;AAAA,EACN,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,EAAE,GAAG,OAAO,SAAS,IAAI;AAClC;AAeO,SAAS,YACd,SAC0C;AAC1C,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB;AAAA,IAChD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAED,QAAM,UAAM;AAAA,IACV,OAAO,SAAY;AACjB,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AACzC,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;AACvC,iBAAS,EAAE,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC;AAC9C,eAAO;AAAA,MACT,SAAS,OAAO;AACd,iBAAS,EAAE,MAAM,MAAM,OAAuB,SAAS,MAAM,CAAC;AAC9D,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,CAAC,KAAK,KAAK;AACpB;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { HttpClient } from '@lyeve/cms-client';
|
|
4
|
+
|
|
5
|
+
interface CmsConfig {
|
|
6
|
+
/** Base URL prepended to every request path. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Callback returning headers added to every request.
|
|
10
|
+
* Called on every request so auth tokens can be refreshed without
|
|
11
|
+
* recreating the provider.
|
|
12
|
+
*/
|
|
13
|
+
getHeaders?: () => Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Context provider that makes the CMS client available to all descendant
|
|
17
|
+
* hooks.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <CmsProvider config={{ baseUrl: 'https://cms.example.com' }}>
|
|
22
|
+
* <App />
|
|
23
|
+
* </CmsProvider>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare function CmsProvider({ config, children, }: {
|
|
27
|
+
config: CmsConfig;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}): react.JSX.Element;
|
|
30
|
+
interface AsyncState<T> {
|
|
31
|
+
data: T | null;
|
|
32
|
+
error: Error | null;
|
|
33
|
+
loading: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reactive query hook. Runs `fetcher` on mount and whenever `deps` change.
|
|
37
|
+
*
|
|
38
|
+
* @param fetcher - Function that receives the configured HttpClient and returns a promise.
|
|
39
|
+
* @param deps - Optional dependency array controlling when to refetch (default: `[]`).
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const { data, error, loading, refetch } = useQuery(
|
|
44
|
+
* (client) => getSchemas(client),
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function useQuery<T>(fetcher: (client: HttpClient) => Promise<T>, deps?: unknown[]): AsyncState<T> & {
|
|
49
|
+
refetch: () => void;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Mutation hook. Returns a trigger function and the current state.
|
|
53
|
+
*
|
|
54
|
+
* @param mutator - Function that receives the configured HttpClient and variables.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const [create, { loading, error }] = useMutation(
|
|
59
|
+
* (client, vars: { title: string }) => createArticle(client, vars),
|
|
60
|
+
* );
|
|
61
|
+
* // await create({ title: 'Hello' })
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function useMutation<T, V>(mutator: (client: HttpClient, vars: V) => Promise<T>): [(vars: V) => Promise<T>, AsyncState<T>];
|
|
65
|
+
|
|
66
|
+
export { type AsyncState, type CmsConfig, CmsProvider, useMutation, useQuery };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { HttpClient } from '@lyeve/cms-client';
|
|
4
|
+
|
|
5
|
+
interface CmsConfig {
|
|
6
|
+
/** Base URL prepended to every request path. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Callback returning headers added to every request.
|
|
10
|
+
* Called on every request so auth tokens can be refreshed without
|
|
11
|
+
* recreating the provider.
|
|
12
|
+
*/
|
|
13
|
+
getHeaders?: () => Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Context provider that makes the CMS client available to all descendant
|
|
17
|
+
* hooks.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <CmsProvider config={{ baseUrl: 'https://cms.example.com' }}>
|
|
22
|
+
* <App />
|
|
23
|
+
* </CmsProvider>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare function CmsProvider({ config, children, }: {
|
|
27
|
+
config: CmsConfig;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}): react.JSX.Element;
|
|
30
|
+
interface AsyncState<T> {
|
|
31
|
+
data: T | null;
|
|
32
|
+
error: Error | null;
|
|
33
|
+
loading: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reactive query hook. Runs `fetcher` on mount and whenever `deps` change.
|
|
37
|
+
*
|
|
38
|
+
* @param fetcher - Function that receives the configured HttpClient and returns a promise.
|
|
39
|
+
* @param deps - Optional dependency array controlling when to refetch (default: `[]`).
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const { data, error, loading, refetch } = useQuery(
|
|
44
|
+
* (client) => getSchemas(client),
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function useQuery<T>(fetcher: (client: HttpClient) => Promise<T>, deps?: unknown[]): AsyncState<T> & {
|
|
49
|
+
refetch: () => void;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Mutation hook. Returns a trigger function and the current state.
|
|
53
|
+
*
|
|
54
|
+
* @param mutator - Function that receives the configured HttpClient and variables.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const [create, { loading, error }] = useMutation(
|
|
59
|
+
* (client, vars: { title: string }) => createArticle(client, vars),
|
|
60
|
+
* );
|
|
61
|
+
* // await create({ title: 'Hello' })
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function useMutation<T, V>(mutator: (client: HttpClient, vars: V) => Promise<T>): [(vars: V) => Promise<T>, AsyncState<T>];
|
|
65
|
+
|
|
66
|
+
export { type AsyncState, type CmsConfig, CmsProvider, useMutation, useQuery };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import { createClient } from "@lyeve/cms-client";
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState
|
|
10
|
+
} from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
var CmsContext = createContext(null);
|
|
13
|
+
function CmsProvider({
|
|
14
|
+
config,
|
|
15
|
+
children
|
|
16
|
+
}) {
|
|
17
|
+
return /* @__PURE__ */ jsx(CmsContext.Provider, { value: config, children });
|
|
18
|
+
}
|
|
19
|
+
function useClient() {
|
|
20
|
+
const config = useContext(CmsContext);
|
|
21
|
+
if (!config) {
|
|
22
|
+
throw new Error("CmsProvider must wrap your component tree");
|
|
23
|
+
}
|
|
24
|
+
return useMemo(() => {
|
|
25
|
+
const base = config.baseUrl ?? "";
|
|
26
|
+
return createClient((url, init) => {
|
|
27
|
+
const fullUrl = typeof url === "string" ? `${base}${url}` : url;
|
|
28
|
+
return fetch(fullUrl, {
|
|
29
|
+
...init,
|
|
30
|
+
headers: { ...init?.headers, ...config.getHeaders?.() }
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}, [config.baseUrl, config.getHeaders]);
|
|
34
|
+
}
|
|
35
|
+
function useQuery(fetcher, deps = []) {
|
|
36
|
+
const client = useClient();
|
|
37
|
+
const [state, setState] = useState({
|
|
38
|
+
data: null,
|
|
39
|
+
error: null,
|
|
40
|
+
loading: true
|
|
41
|
+
});
|
|
42
|
+
const run = useCallback(() => {
|
|
43
|
+
setState((s) => ({ ...s, loading: true }));
|
|
44
|
+
fetcher(client).then((data) => setState({ data, error: null, loading: false })).catch(
|
|
45
|
+
(error) => setState((s) => ({ ...s, error, loading: false }))
|
|
46
|
+
);
|
|
47
|
+
}, [client, ...deps]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
run();
|
|
50
|
+
}, [run]);
|
|
51
|
+
return { ...state, refetch: run };
|
|
52
|
+
}
|
|
53
|
+
function useMutation(mutator) {
|
|
54
|
+
const client = useClient();
|
|
55
|
+
const [state, setState] = useState({
|
|
56
|
+
data: null,
|
|
57
|
+
error: null,
|
|
58
|
+
loading: false
|
|
59
|
+
});
|
|
60
|
+
const run = useCallback(
|
|
61
|
+
async (vars) => {
|
|
62
|
+
setState((s) => ({ ...s, loading: true }));
|
|
63
|
+
try {
|
|
64
|
+
const data = await mutator(client, vars);
|
|
65
|
+
setState({ data, error: null, loading: false });
|
|
66
|
+
return data;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
setState({ data: null, error, loading: false });
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[client]
|
|
73
|
+
);
|
|
74
|
+
return [run, state];
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
CmsProvider,
|
|
78
|
+
useMutation,
|
|
79
|
+
useQuery
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["/**\n * LyEve CMS React hooks - typed, reactive data fetching.\n *\n * Thin wrapper around {@link @lyeve/cms-client} that makes the client\n * available via context and provides `useQuery` / `useMutation` hooks.\n *\n * @example\n * ```tsx\n * import { CmsProvider, useQuery } from '@lyeve/cms-client-react';\n * import { getSchemas } from '@lyeve/cms-client-rest';\n *\n * function App() {\n * return (\n * <CmsProvider config={{ baseUrl: 'https://cms.example.com', getHeaders: () => ({ Authorization: `Bearer ${token}` }) }}>\n * <SchemaManager />\n * </CmsProvider>\n * );\n * }\n *\n * function SchemaManager() {\n * const { data: schemas, loading } = useQuery((client) => getSchemas(client));\n * // ...\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createClient, type HttpClient } from '@lyeve/cms-client';\nimport {\n createContext,\n useContext,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\n// Provider\n\nexport interface CmsConfig {\n /** Base URL prepended to every request path. */\n baseUrl?: string;\n /**\n * Callback returning headers added to every request.\n * Called on every request so auth tokens can be refreshed without\n * recreating the provider.\n */\n getHeaders?: () => Record<string, string>;\n}\n\nconst CmsContext = createContext<CmsConfig | null>(null);\n\n/**\n * Context provider that makes the CMS client available to all descendant\n * hooks.\n *\n * @example\n * ```tsx\n * <CmsProvider config={{ baseUrl: 'https://cms.example.com' }}>\n * <App />\n * </CmsProvider>\n * ```\n */\nexport function CmsProvider({\n config,\n children,\n}: {\n config: CmsConfig;\n children: ReactNode;\n}) {\n return <CmsContext.Provider value={config}>{children}</CmsContext.Provider>;\n}\n\n/**\n * Returns a memoized HttpClient instance configured from the nearest\n * {@link CmsProvider}.\n */\nfunction useClient(): HttpClient {\n const config = useContext(CmsContext);\n if (!config) {\n throw new Error('CmsProvider must wrap your component tree');\n }\n // Re-create when config changes so getHeaders/auth tokens stay current.\n return useMemo(() => {\n const base = config.baseUrl ?? '';\n return createClient((url, init) => {\n const fullUrl = typeof url === 'string' ? `${base}${url}` : url;\n return fetch(fullUrl, {\n ...init,\n headers: { ...init?.headers, ...config.getHeaders?.() },\n });\n });\n }, [config.baseUrl, config.getHeaders]);\n}\n\n// Hooks\n\nexport interface AsyncState<T> {\n data: T | null;\n error: Error | null;\n loading: boolean;\n}\n\n/**\n * Reactive query hook. Runs `fetcher` on mount and whenever `deps` change.\n *\n * @param fetcher - Function that receives the configured HttpClient and returns a promise.\n * @param deps - Optional dependency array controlling when to refetch (default: `[]`).\n *\n * @example\n * ```ts\n * const { data, error, loading, refetch } = useQuery(\n * (client) => getSchemas(client),\n * );\n * ```\n */\nexport function useQuery<T>(\n fetcher: (client: HttpClient) => Promise<T>,\n deps: unknown[] = [],\n): AsyncState<T> & { refetch: () => void } {\n const client = useClient();\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n error: null,\n loading: true,\n });\n\n const run = useCallback(() => {\n setState((s) => ({ ...s, loading: true }));\n\n fetcher(client)\n .then((data) => setState({ data, error: null, loading: false }))\n .catch((error) =>\n setState((s) => ({ ...s, error: error as Error, loading: false })),\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, ...deps]);\n\n useEffect(() => {\n run();\n }, [run]);\n\n return { ...state, refetch: run };\n}\n\n/**\n * Mutation hook. Returns a trigger function and the current state.\n *\n * @param mutator - Function that receives the configured HttpClient and variables.\n *\n * @example\n * ```ts\n * const [create, { loading, error }] = useMutation(\n * (client, vars: { title: string }) => createArticle(client, vars),\n * );\n * // await create({ title: 'Hello' })\n * ```\n */\nexport function useMutation<T, V>(\n mutator: (client: HttpClient, vars: V) => Promise<T>,\n): [(vars: V) => Promise<T>, AsyncState<T>] {\n const client = useClient();\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n error: null,\n loading: false,\n });\n\n const run = useCallback(\n async (vars: V) => {\n setState((s) => ({ ...s, loading: true }));\n try {\n const data = await mutator(client, vars);\n setState({ data, error: null, loading: false });\n return data;\n } catch (error) {\n setState({ data: null, error: error as Error, loading: false });\n throw error;\n }\n },\n [client],\n );\n\n return [run, state];\n}\n"],"mappings":";AA4BA,SAAS,oBAAqC;AAC9C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAmCE;AApBT,IAAM,aAAa,cAAgC,IAAI;AAahD,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AACF,GAGG;AACD,SAAO,oBAAC,WAAW,UAAX,EAAoB,OAAO,QAAS,UAAS;AACvD;AAMA,SAAS,YAAwB;AAC/B,QAAM,SAAS,WAAW,UAAU;AACpC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AAEA,SAAO,QAAQ,MAAM;AACnB,UAAM,OAAO,OAAO,WAAW;AAC/B,WAAO,aAAa,CAAC,KAAK,SAAS;AACjC,YAAM,UAAU,OAAO,QAAQ,WAAW,GAAG,IAAI,GAAG,GAAG,KAAK;AAC5D,aAAO,MAAM,SAAS;AAAA,QACpB,GAAG;AAAA,QACH,SAAS,EAAE,GAAG,MAAM,SAAS,GAAG,OAAO,aAAa,EAAE;AAAA,MACxD,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,SAAS,OAAO,UAAU,CAAC;AACxC;AAuBO,SAAS,SACd,SACA,OAAkB,CAAC,GACsB;AACzC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB;AAAA,IAChD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAED,QAAM,MAAM,YAAY,MAAM;AAC5B,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AAEzC,YAAQ,MAAM,EACX,KAAK,CAAC,SAAS,SAAS,EAAE,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,EAC9D;AAAA,MAAM,CAAC,UACN,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAuB,SAAS,MAAM,EAAE;AAAA,IACnE;AAAA,EAEJ,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;AAEpB,YAAU,MAAM;AACd,QAAI;AAAA,EACN,GAAG,CAAC,GAAG,CAAC;AAER,SAAO,EAAE,GAAG,OAAO,SAAS,IAAI;AAClC;AAeO,SAAS,YACd,SAC0C;AAC1C,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB;AAAA,IAChD,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAED,QAAM,MAAM;AAAA,IACV,OAAO,SAAY;AACjB,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AACzC,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI;AACvC,iBAAS,EAAE,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC;AAC9C,eAAO;AAAA,MACT,SAAS,OAAO;AACd,iBAAS,EAAE,MAAM,MAAM,OAAuB,SAAS,MAAM,CAAC;AAC9D,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,CAAC,KAAK,KAAK;AACpB;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lyeve-labs/client-react",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "React hooks for LyEve Core - typed, reactive data fetching",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "LyEve Labs <hello@lyeve.com>",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"require": "./dist/index.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"!dist/**/*.test.*",
|
|
26
|
+
"!dist/**/*.spec.*"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@lyeve-labs/client-react": ">=0.1.0",
|
|
33
|
+
"react": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@lyeve-labs/client-react": "^0.1.0",
|
|
37
|
+
"@testing-library/react": "^16.3.2",
|
|
38
|
+
"@types/react": "^19.2.0",
|
|
39
|
+
"jsdom": "^29.1.1",
|
|
40
|
+
"prettier": "^3.4.0",
|
|
41
|
+
"publint": "^0.3.0",
|
|
42
|
+
"react": "^19.2.8",
|
|
43
|
+
"react-dom": "^19.2.8",
|
|
44
|
+
"tsup": "^8.4.0",
|
|
45
|
+
"typescript": "^5.7.0",
|
|
46
|
+
"vitest": "^2.1.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup && publint",
|
|
50
|
+
"check": "tsc --noEmit",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"dev": "tsup --watch",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"format:check": "prettier --check ."
|
|
56
|
+
}
|
|
57
|
+
}
|