@dispatchcms/react 0.0.5 → 0.0.7
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/.turbo/turbo-build.log +5 -5
- package/README.md +23 -24
- package/dist/index.d.mts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.js +35 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -9
- package/src/hooks.ts +37 -30
- package/src/index.ts +3 -3
- package/src/types.ts +6 -6
package/.turbo/turbo-build.log
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
[34mCLI[39m Cleaning output folder
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
13
|
[34mESM[39m Build start
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m3.49 KB[39m
|
|
15
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m7.08 KB[39m
|
|
16
|
-
[32mESM[39m ⚡️ Build success in 13ms
|
|
17
14
|
[32mCJS[39m [1mdist/index.js [22m[32m4.80 KB[39m
|
|
18
15
|
[32mCJS[39m [1mdist/index.js.map [22m[32m7.38 KB[39m
|
|
19
|
-
[32mCJS[39m ⚡️ Build success in
|
|
16
|
+
[32mCJS[39m ⚡️ Build success in 12ms
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m3.49 KB[39m
|
|
18
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m7.08 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 12ms
|
|
20
20
|
DTS Build start
|
|
21
|
-
DTS ⚡️ Build success in
|
|
21
|
+
DTS ⚡️ Build success in 442ms
|
|
22
22
|
DTS dist/index.d.ts 1.10 KB
|
|
23
23
|
DTS dist/index.d.mts 1.10 KB
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @dispatchcms/react
|
|
2
2
|
|
|
3
|
-
React hooks and provider for [Dispatch](https://dispatch-cms.vercel.app) CMS. Use `DispatchProvider` and `
|
|
3
|
+
React hooks and provider for [Dispatch](https://dispatch-cms.vercel.app) CMS. Use `DispatchProvider` and `useEntry` / `useEntries` to fetch published entries in any React app (Next.js, Vite, etc.).
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -32,29 +32,29 @@ To use a different API base URL, pass the `apiBase` prop to the provider.
|
|
|
32
32
|
|
|
33
33
|
## API
|
|
34
34
|
|
|
35
|
-
### `
|
|
35
|
+
### `useEntry(collection, slug)`
|
|
36
36
|
|
|
37
|
-
Fetches a single published
|
|
37
|
+
Fetches a single published entry.
|
|
38
38
|
|
|
39
|
-
- **Returns:** `{
|
|
40
|
-
- On failure or missing site key, does not throw; returns `
|
|
39
|
+
- **Returns:** `{ entry: Entry | null, isLoading: boolean, error?: string }`
|
|
40
|
+
- On failure or missing site key, does not throw; returns `entry: null`, `isLoading: false`, and `error` set.
|
|
41
41
|
|
|
42
|
-
### `
|
|
42
|
+
### `useEntries(collection)`
|
|
43
43
|
|
|
44
|
-
Fetches
|
|
44
|
+
Fetches published entries for a collection.
|
|
45
45
|
|
|
46
|
-
- **Returns:** `{
|
|
47
|
-
- On failure or missing site key, does not throw; returns `
|
|
46
|
+
- **Returns:** `{ entries: Entry[], isLoading: boolean, error?: string }`
|
|
47
|
+
- On failure or missing site key, does not throw; returns `entries: []`, `isLoading: false`, and `error` set.
|
|
48
48
|
|
|
49
49
|
### Graceful failure
|
|
50
50
|
|
|
51
|
-
- If the API request fails, hooks return `
|
|
51
|
+
- If the API request fails, hooks return `entry: null` / `entries: []`, `isLoading: false`, and `error: "Failed to load"`. The React tree is never crashed.
|
|
52
52
|
- If no `siteKey` is provided to the provider, `console.warn` is logged and hooks return the same safe empty/error state with `error: "Missing site key"`.
|
|
53
53
|
|
|
54
54
|
## Types
|
|
55
55
|
|
|
56
|
-
- **`
|
|
57
|
-
- **`
|
|
56
|
+
- **`Entry`** – Entry from a collection. Fields include `id`, `collection`, `title`, `slug`, `data`, `published`, `published_at`, `sort_order`. Rich text values inside `data` are **TipTap (ProseMirror) JSON**.
|
|
57
|
+
- **`UseEntryResult`** / **`UseEntriesResult`** – Return types of the hooks.
|
|
58
58
|
|
|
59
59
|
## Rendering content
|
|
60
60
|
|
|
@@ -78,26 +78,26 @@ function TipTapContent({ content }: { content: unknown }) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// In your post page:
|
|
81
|
-
<TipTapContent content={
|
|
81
|
+
<TipTapContent content={entry.data.body} />
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
## Example
|
|
85
85
|
|
|
86
86
|
```tsx
|
|
87
87
|
// List posts (e.g. homepage)
|
|
88
|
-
import {
|
|
88
|
+
import { useEntries } from "@dispatchcms/react";
|
|
89
89
|
|
|
90
90
|
export default function HomePage() {
|
|
91
|
-
const {
|
|
91
|
+
const { entries, isLoading, error } = useEntries("posts");
|
|
92
92
|
|
|
93
93
|
if (isLoading) return <p>Loading…</p>;
|
|
94
94
|
if (error) return <p>{error}</p>;
|
|
95
95
|
|
|
96
96
|
return (
|
|
97
97
|
<ul>
|
|
98
|
-
{
|
|
99
|
-
<li key={
|
|
100
|
-
<a href={`/blog/${
|
|
98
|
+
{entries.map((entry) => (
|
|
99
|
+
<li key={entry.id}>
|
|
100
|
+
<a href={`/blog/${entry.slug}`}>{entry.title}</a>
|
|
101
101
|
</li>
|
|
102
102
|
))}
|
|
103
103
|
</ul>
|
|
@@ -107,20 +107,19 @@ export default function HomePage() {
|
|
|
107
107
|
|
|
108
108
|
```tsx
|
|
109
109
|
// Single post page (e.g. /blog/:slug)
|
|
110
|
-
import {
|
|
110
|
+
import { useEntry } from "@dispatchcms/react";
|
|
111
111
|
import { TipTapContent } from "./TipTapContent"; // or inline renderToReactElement
|
|
112
112
|
|
|
113
113
|
export default function PostPage({ slug }) {
|
|
114
|
-
const {
|
|
114
|
+
const { entry, isLoading, error } = useEntry("posts", slug);
|
|
115
115
|
|
|
116
116
|
if (isLoading) return <p>Loading…</p>;
|
|
117
|
-
if (error || !
|
|
117
|
+
if (error || !entry) return <p>{error ?? "Not found"}</p>;
|
|
118
118
|
|
|
119
119
|
return (
|
|
120
120
|
<article>
|
|
121
|
-
<h1>{
|
|
122
|
-
|
|
123
|
-
<TipTapContent content={post.content} />
|
|
121
|
+
<h1>{entry.title}</h1>
|
|
122
|
+
<TipTapContent content={entry.data.body} />
|
|
124
123
|
</article>
|
|
125
124
|
);
|
|
126
125
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
type Entry = {
|
|
4
4
|
id: string;
|
|
5
5
|
created_at: string;
|
|
6
|
-
published_at: string;
|
|
6
|
+
published_at: string | null;
|
|
7
7
|
updated_at: string;
|
|
8
8
|
site_id: string;
|
|
9
|
+
collection: string;
|
|
9
10
|
title: string;
|
|
10
|
-
slug: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
featured_image: string | null;
|
|
11
|
+
slug: string | null;
|
|
12
|
+
data: Record<string, unknown>;
|
|
13
|
+
sort_order: number;
|
|
14
14
|
published: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -26,17 +26,17 @@ type DispatchProviderProps = {
|
|
|
26
26
|
};
|
|
27
27
|
declare function DispatchProvider({ siteKey, apiBase, children, }: DispatchProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
28
|
|
|
29
|
-
type
|
|
30
|
-
|
|
29
|
+
type UseEntryResult = {
|
|
30
|
+
entry: Entry | null;
|
|
31
31
|
isLoading: boolean;
|
|
32
32
|
error?: string;
|
|
33
33
|
};
|
|
34
|
-
declare function
|
|
35
|
-
type
|
|
36
|
-
|
|
34
|
+
declare function useEntry(collection: string | undefined, slug: string | undefined): UseEntryResult;
|
|
35
|
+
type UseEntriesResult = {
|
|
36
|
+
entries: Entry[];
|
|
37
37
|
isLoading: boolean;
|
|
38
38
|
error?: string;
|
|
39
39
|
};
|
|
40
|
-
declare function
|
|
40
|
+
declare function useEntries(collection: string | undefined): UseEntriesResult;
|
|
41
41
|
|
|
42
|
-
export { DispatchProvider, type
|
|
42
|
+
export { DispatchProvider, type Entry, type UseEntriesResult, type UseEntryResult, useDispatchContext, useEntries, useEntry };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
type Entry = {
|
|
4
4
|
id: string;
|
|
5
5
|
created_at: string;
|
|
6
|
-
published_at: string;
|
|
6
|
+
published_at: string | null;
|
|
7
7
|
updated_at: string;
|
|
8
8
|
site_id: string;
|
|
9
|
+
collection: string;
|
|
9
10
|
title: string;
|
|
10
|
-
slug: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
featured_image: string | null;
|
|
11
|
+
slug: string | null;
|
|
12
|
+
data: Record<string, unknown>;
|
|
13
|
+
sort_order: number;
|
|
14
14
|
published: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -26,17 +26,17 @@ type DispatchProviderProps = {
|
|
|
26
26
|
};
|
|
27
27
|
declare function DispatchProvider({ siteKey, apiBase, children, }: DispatchProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
28
|
|
|
29
|
-
type
|
|
30
|
-
|
|
29
|
+
type UseEntryResult = {
|
|
30
|
+
entry: Entry | null;
|
|
31
31
|
isLoading: boolean;
|
|
32
32
|
error?: string;
|
|
33
33
|
};
|
|
34
|
-
declare function
|
|
35
|
-
type
|
|
36
|
-
|
|
34
|
+
declare function useEntry(collection: string | undefined, slug: string | undefined): UseEntryResult;
|
|
35
|
+
type UseEntriesResult = {
|
|
36
|
+
entries: Entry[];
|
|
37
37
|
isLoading: boolean;
|
|
38
38
|
error?: string;
|
|
39
39
|
};
|
|
40
|
-
declare function
|
|
40
|
+
declare function useEntries(collection: string | undefined): UseEntriesResult;
|
|
41
41
|
|
|
42
|
-
export { DispatchProvider, type
|
|
42
|
+
export { DispatchProvider, type Entry, type UseEntriesResult, type UseEntryResult, useDispatchContext, useEntries, useEntry };
|
package/dist/index.js
CHANGED
|
@@ -23,8 +23,8 @@ var index_exports = {};
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
DispatchProvider: () => DispatchProvider,
|
|
25
25
|
useDispatchContext: () => useDispatchContext,
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
useEntries: () => useEntries,
|
|
27
|
+
useEntry: () => useEntry
|
|
28
28
|
});
|
|
29
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
30
|
|
|
@@ -64,110 +64,116 @@ function DispatchProvider({
|
|
|
64
64
|
|
|
65
65
|
// src/hooks.ts
|
|
66
66
|
var import_react2 = require("react");
|
|
67
|
-
function
|
|
67
|
+
function useEntry(collection, slug) {
|
|
68
68
|
const ctx = useDispatchContext();
|
|
69
|
-
const [
|
|
69
|
+
const [entry, setEntry] = (0, import_react2.useState)(null);
|
|
70
70
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
71
71
|
const [error, setError] = (0, import_react2.useState)(void 0);
|
|
72
72
|
(0, import_react2.useEffect)(() => {
|
|
73
|
-
if (!slug?.trim()) {
|
|
74
|
-
|
|
73
|
+
if (!collection?.trim() || !slug?.trim()) {
|
|
74
|
+
setEntry(null);
|
|
75
75
|
setIsLoading(false);
|
|
76
76
|
setError(void 0);
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
if (!ctx?.siteKey) {
|
|
80
|
-
|
|
80
|
+
setEntry(null);
|
|
81
81
|
setIsLoading(false);
|
|
82
82
|
setError("Missing site key");
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
let cancelled = false;
|
|
86
|
-
|
|
86
|
+
setEntry(null);
|
|
87
87
|
setError(void 0);
|
|
88
88
|
setIsLoading(true);
|
|
89
|
-
const url = `${ctx.apiBase}/api/
|
|
89
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}/${encodeURIComponent(slug.trim())}`;
|
|
90
90
|
fetch(url, {
|
|
91
91
|
headers: { "X-Site-Key": ctx.siteKey }
|
|
92
92
|
}).then((res) => {
|
|
93
93
|
if (cancelled) return;
|
|
94
94
|
if (res.status === 404) {
|
|
95
|
-
|
|
95
|
+
setEntry(null);
|
|
96
96
|
setIsLoading(false);
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
if (!res.ok) {
|
|
100
|
-
|
|
100
|
+
setEntry(null);
|
|
101
101
|
setIsLoading(false);
|
|
102
102
|
setError("Failed to load");
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
105
|
return res.json();
|
|
106
106
|
}).then((data) => {
|
|
107
|
-
if (cancelled) return;
|
|
108
|
-
|
|
107
|
+
if (cancelled || data == null) return;
|
|
108
|
+
setEntry(data);
|
|
109
109
|
setIsLoading(false);
|
|
110
110
|
}).catch(() => {
|
|
111
111
|
if (cancelled) return;
|
|
112
|
-
|
|
112
|
+
setEntry(null);
|
|
113
113
|
setIsLoading(false);
|
|
114
114
|
setError("Failed to load");
|
|
115
115
|
});
|
|
116
116
|
return () => {
|
|
117
117
|
cancelled = true;
|
|
118
118
|
};
|
|
119
|
-
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
120
|
-
return {
|
|
119
|
+
}, [collection, slug, ctx?.siteKey, ctx?.apiBase]);
|
|
120
|
+
return { entry, isLoading, error };
|
|
121
121
|
}
|
|
122
|
-
function
|
|
122
|
+
function useEntries(collection) {
|
|
123
123
|
const ctx = useDispatchContext();
|
|
124
|
-
const [
|
|
124
|
+
const [entries, setEntries] = (0, import_react2.useState)([]);
|
|
125
125
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
126
126
|
const [error, setError] = (0, import_react2.useState)(void 0);
|
|
127
127
|
(0, import_react2.useEffect)(() => {
|
|
128
|
+
if (!collection?.trim()) {
|
|
129
|
+
setEntries([]);
|
|
130
|
+
setIsLoading(false);
|
|
131
|
+
setError(void 0);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
128
134
|
if (!ctx?.siteKey) {
|
|
129
|
-
|
|
135
|
+
setEntries([]);
|
|
130
136
|
setIsLoading(false);
|
|
131
137
|
setError("Missing site key");
|
|
132
138
|
return;
|
|
133
139
|
}
|
|
134
140
|
let cancelled = false;
|
|
135
|
-
|
|
141
|
+
setEntries([]);
|
|
136
142
|
setError(void 0);
|
|
137
143
|
setIsLoading(true);
|
|
138
|
-
const url = `${ctx.apiBase}/api/
|
|
144
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}`;
|
|
139
145
|
fetch(url, {
|
|
140
146
|
headers: { "X-Site-Key": ctx.siteKey }
|
|
141
147
|
}).then((res) => {
|
|
142
148
|
if (cancelled) return;
|
|
143
149
|
if (!res.ok) {
|
|
144
|
-
|
|
150
|
+
setEntries([]);
|
|
145
151
|
setIsLoading(false);
|
|
146
152
|
setError("Failed to load");
|
|
147
153
|
return;
|
|
148
154
|
}
|
|
149
155
|
return res.json();
|
|
150
156
|
}).then((data) => {
|
|
151
|
-
if (cancelled) return;
|
|
152
|
-
|
|
157
|
+
if (cancelled || data == null) return;
|
|
158
|
+
setEntries(Array.isArray(data) ? data : []);
|
|
153
159
|
setIsLoading(false);
|
|
154
160
|
}).catch(() => {
|
|
155
161
|
if (cancelled) return;
|
|
156
|
-
|
|
162
|
+
setEntries([]);
|
|
157
163
|
setIsLoading(false);
|
|
158
164
|
setError("Failed to load");
|
|
159
165
|
});
|
|
160
166
|
return () => {
|
|
161
167
|
cancelled = true;
|
|
162
168
|
};
|
|
163
|
-
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
164
|
-
return {
|
|
169
|
+
}, [collection, ctx?.siteKey, ctx?.apiBase]);
|
|
170
|
+
return { entries, isLoading, error };
|
|
165
171
|
}
|
|
166
172
|
// Annotate the CommonJS export names for ESM import in node:
|
|
167
173
|
0 && (module.exports = {
|
|
168
174
|
DispatchProvider,
|
|
169
175
|
useDispatchContext,
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
useEntries,
|
|
177
|
+
useEntry
|
|
172
178
|
});
|
|
173
179
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/config.ts","../src/hooks.ts"],"sourcesContent":["export type {
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/config.ts","../src/hooks.ts"],"sourcesContent":["export type { Entry } from \"./types\";\nexport { DispatchProvider, useDispatchContext } from \"./context\";\nexport { useEntry, useEntries } from \"./hooks\";\nexport type { UseEntryResult, UseEntriesResult } from \"./hooks\";\n","\"use client\";\n\nimport React, { createContext, useContext } from \"react\";\nimport { getApiBase } from \"./config\";\n\ntype DispatchContextValue = {\n siteKey: string | null;\n apiBase: string;\n};\n\nconst DispatchContext = createContext<DispatchContextValue | null>(null);\n\nexport function useDispatchContext(): DispatchContextValue | null {\n return useContext(DispatchContext);\n}\n\ntype DispatchProviderProps = {\n siteKey?: string | null;\n apiBase?: string;\n children?: unknown;\n};\n\nexport function DispatchProvider({\n siteKey = null,\n apiBase = getApiBase(),\n children,\n}: DispatchProviderProps) {\n const value: DispatchContextValue = {\n siteKey: siteKey?.trim() || null,\n apiBase,\n };\n\n if (!value.siteKey) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n \"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider. Hooks will return empty/error state.\"\n );\n }\n }\n\n return (\n <DispatchContext.Provider value={value}>\n {children as React.ReactNode}\n </DispatchContext.Provider>\n );\n}\n","const DEFAULT_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport function getApiBase(): string {\n return DEFAULT_API_BASE;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { Entry } from \"./types\";\nimport { useDispatchContext } from \"./context\";\n\nexport type UseEntryResult = {\n entry: Entry | null;\n isLoading: boolean;\n error?: string;\n};\n\nexport function useEntry(collection: string | undefined, slug: string | undefined): UseEntryResult {\n const ctx = useDispatchContext();\n const [entry, setEntry] = useState<Entry | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!collection?.trim() || !slug?.trim()) {\n setEntry(null);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setEntry(null);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setEntry(null);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}/${encodeURIComponent(slug.trim())}`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (res.status === 404) {\n setEntry(null);\n setIsLoading(false);\n return;\n }\n if (!res.ok) {\n setEntry(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled || data == null) return;\n setEntry(data as Entry);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setEntry(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [collection, slug, ctx?.siteKey, ctx?.apiBase]);\n\n return { entry, isLoading, error };\n}\n\nexport type UseEntriesResult = {\n entries: Entry[];\n isLoading: boolean;\n error?: string;\n};\n\nexport function useEntries(collection: string | undefined): UseEntriesResult {\n const ctx = useDispatchContext();\n const [entries, setEntries] = useState<Entry[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!collection?.trim()) {\n setEntries([]);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setEntries([]);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setEntries([]);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (!res.ok) {\n setEntries([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled || data == null) return;\n setEntries(Array.isArray(data) ? data : []);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setEntries([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [collection, ctx?.siteKey, ctx?.apiBase]);\n\n return { entries, isLoading, error };\n}\n"],"mappings":"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","names":["import_react"]}
|
package/dist/index.mjs
CHANGED
|
@@ -36,109 +36,115 @@ function DispatchProvider({
|
|
|
36
36
|
|
|
37
37
|
// src/hooks.ts
|
|
38
38
|
import { useEffect, useState } from "react";
|
|
39
|
-
function
|
|
39
|
+
function useEntry(collection, slug) {
|
|
40
40
|
const ctx = useDispatchContext();
|
|
41
|
-
const [
|
|
41
|
+
const [entry, setEntry] = useState(null);
|
|
42
42
|
const [isLoading, setIsLoading] = useState(true);
|
|
43
43
|
const [error, setError] = useState(void 0);
|
|
44
44
|
useEffect(() => {
|
|
45
|
-
if (!slug?.trim()) {
|
|
46
|
-
|
|
45
|
+
if (!collection?.trim() || !slug?.trim()) {
|
|
46
|
+
setEntry(null);
|
|
47
47
|
setIsLoading(false);
|
|
48
48
|
setError(void 0);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
if (!ctx?.siteKey) {
|
|
52
|
-
|
|
52
|
+
setEntry(null);
|
|
53
53
|
setIsLoading(false);
|
|
54
54
|
setError("Missing site key");
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
let cancelled = false;
|
|
58
|
-
|
|
58
|
+
setEntry(null);
|
|
59
59
|
setError(void 0);
|
|
60
60
|
setIsLoading(true);
|
|
61
|
-
const url = `${ctx.apiBase}/api/
|
|
61
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}/${encodeURIComponent(slug.trim())}`;
|
|
62
62
|
fetch(url, {
|
|
63
63
|
headers: { "X-Site-Key": ctx.siteKey }
|
|
64
64
|
}).then((res) => {
|
|
65
65
|
if (cancelled) return;
|
|
66
66
|
if (res.status === 404) {
|
|
67
|
-
|
|
67
|
+
setEntry(null);
|
|
68
68
|
setIsLoading(false);
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
if (!res.ok) {
|
|
72
|
-
|
|
72
|
+
setEntry(null);
|
|
73
73
|
setIsLoading(false);
|
|
74
74
|
setError("Failed to load");
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
return res.json();
|
|
78
78
|
}).then((data) => {
|
|
79
|
-
if (cancelled) return;
|
|
80
|
-
|
|
79
|
+
if (cancelled || data == null) return;
|
|
80
|
+
setEntry(data);
|
|
81
81
|
setIsLoading(false);
|
|
82
82
|
}).catch(() => {
|
|
83
83
|
if (cancelled) return;
|
|
84
|
-
|
|
84
|
+
setEntry(null);
|
|
85
85
|
setIsLoading(false);
|
|
86
86
|
setError("Failed to load");
|
|
87
87
|
});
|
|
88
88
|
return () => {
|
|
89
89
|
cancelled = true;
|
|
90
90
|
};
|
|
91
|
-
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
92
|
-
return {
|
|
91
|
+
}, [collection, slug, ctx?.siteKey, ctx?.apiBase]);
|
|
92
|
+
return { entry, isLoading, error };
|
|
93
93
|
}
|
|
94
|
-
function
|
|
94
|
+
function useEntries(collection) {
|
|
95
95
|
const ctx = useDispatchContext();
|
|
96
|
-
const [
|
|
96
|
+
const [entries, setEntries] = useState([]);
|
|
97
97
|
const [isLoading, setIsLoading] = useState(true);
|
|
98
98
|
const [error, setError] = useState(void 0);
|
|
99
99
|
useEffect(() => {
|
|
100
|
+
if (!collection?.trim()) {
|
|
101
|
+
setEntries([]);
|
|
102
|
+
setIsLoading(false);
|
|
103
|
+
setError(void 0);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
100
106
|
if (!ctx?.siteKey) {
|
|
101
|
-
|
|
107
|
+
setEntries([]);
|
|
102
108
|
setIsLoading(false);
|
|
103
109
|
setError("Missing site key");
|
|
104
110
|
return;
|
|
105
111
|
}
|
|
106
112
|
let cancelled = false;
|
|
107
|
-
|
|
113
|
+
setEntries([]);
|
|
108
114
|
setError(void 0);
|
|
109
115
|
setIsLoading(true);
|
|
110
|
-
const url = `${ctx.apiBase}/api/
|
|
116
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}`;
|
|
111
117
|
fetch(url, {
|
|
112
118
|
headers: { "X-Site-Key": ctx.siteKey }
|
|
113
119
|
}).then((res) => {
|
|
114
120
|
if (cancelled) return;
|
|
115
121
|
if (!res.ok) {
|
|
116
|
-
|
|
122
|
+
setEntries([]);
|
|
117
123
|
setIsLoading(false);
|
|
118
124
|
setError("Failed to load");
|
|
119
125
|
return;
|
|
120
126
|
}
|
|
121
127
|
return res.json();
|
|
122
128
|
}).then((data) => {
|
|
123
|
-
if (cancelled) return;
|
|
124
|
-
|
|
129
|
+
if (cancelled || data == null) return;
|
|
130
|
+
setEntries(Array.isArray(data) ? data : []);
|
|
125
131
|
setIsLoading(false);
|
|
126
132
|
}).catch(() => {
|
|
127
133
|
if (cancelled) return;
|
|
128
|
-
|
|
134
|
+
setEntries([]);
|
|
129
135
|
setIsLoading(false);
|
|
130
136
|
setError("Failed to load");
|
|
131
137
|
});
|
|
132
138
|
return () => {
|
|
133
139
|
cancelled = true;
|
|
134
140
|
};
|
|
135
|
-
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
136
|
-
return {
|
|
141
|
+
}, [collection, ctx?.siteKey, ctx?.apiBase]);
|
|
142
|
+
return { entries, isLoading, error };
|
|
137
143
|
}
|
|
138
144
|
export {
|
|
139
145
|
DispatchProvider,
|
|
140
146
|
useDispatchContext,
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
useEntries,
|
|
148
|
+
useEntry
|
|
143
149
|
};
|
|
144
150
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/config.ts","../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext } from \"react\";\nimport { getApiBase } from \"./config\";\n\ntype DispatchContextValue = {\n siteKey: string | null;\n apiBase: string;\n};\n\nconst DispatchContext = createContext<DispatchContextValue | null>(null);\n\nexport function useDispatchContext(): DispatchContextValue | null {\n return useContext(DispatchContext);\n}\n\ntype DispatchProviderProps = {\n siteKey?: string | null;\n apiBase?: string;\n children?: unknown;\n};\n\nexport function DispatchProvider({\n siteKey = null,\n apiBase = getApiBase(),\n children,\n}: DispatchProviderProps) {\n const value: DispatchContextValue = {\n siteKey: siteKey?.trim() || null,\n apiBase,\n };\n\n if (!value.siteKey) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n \"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider. Hooks will return empty/error state.\"\n );\n }\n }\n\n return (\n <DispatchContext.Provider value={value}>\n {children as React.ReactNode}\n </DispatchContext.Provider>\n );\n}\n","const DEFAULT_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport function getApiBase(): string {\n return DEFAULT_API_BASE;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/config.ts","../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext } from \"react\";\nimport { getApiBase } from \"./config\";\n\ntype DispatchContextValue = {\n siteKey: string | null;\n apiBase: string;\n};\n\nconst DispatchContext = createContext<DispatchContextValue | null>(null);\n\nexport function useDispatchContext(): DispatchContextValue | null {\n return useContext(DispatchContext);\n}\n\ntype DispatchProviderProps = {\n siteKey?: string | null;\n apiBase?: string;\n children?: unknown;\n};\n\nexport function DispatchProvider({\n siteKey = null,\n apiBase = getApiBase(),\n children,\n}: DispatchProviderProps) {\n const value: DispatchContextValue = {\n siteKey: siteKey?.trim() || null,\n apiBase,\n };\n\n if (!value.siteKey) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n \"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider. Hooks will return empty/error state.\"\n );\n }\n }\n\n return (\n <DispatchContext.Provider value={value}>\n {children as React.ReactNode}\n </DispatchContext.Provider>\n );\n}\n","const DEFAULT_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport function getApiBase(): string {\n return DEFAULT_API_BASE;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { Entry } from \"./types\";\nimport { useDispatchContext } from \"./context\";\n\nexport type UseEntryResult = {\n entry: Entry | null;\n isLoading: boolean;\n error?: string;\n};\n\nexport function useEntry(collection: string | undefined, slug: string | undefined): UseEntryResult {\n const ctx = useDispatchContext();\n const [entry, setEntry] = useState<Entry | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!collection?.trim() || !slug?.trim()) {\n setEntry(null);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setEntry(null);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setEntry(null);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}/${encodeURIComponent(slug.trim())}`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (res.status === 404) {\n setEntry(null);\n setIsLoading(false);\n return;\n }\n if (!res.ok) {\n setEntry(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled || data == null) return;\n setEntry(data as Entry);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setEntry(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [collection, slug, ctx?.siteKey, ctx?.apiBase]);\n\n return { entry, isLoading, error };\n}\n\nexport type UseEntriesResult = {\n entries: Entry[];\n isLoading: boolean;\n error?: string;\n};\n\nexport function useEntries(collection: string | undefined): UseEntriesResult {\n const ctx = useDispatchContext();\n const [entries, setEntries] = useState<Entry[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!collection?.trim()) {\n setEntries([]);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setEntries([]);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setEntries([]);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (!res.ok) {\n setEntries([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled || data == null) return;\n setEntries(Array.isArray(data) ? data : []);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setEntries([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [collection, ctx?.siteKey, ctx?.apiBase]);\n\n return { entries, isLoading, error };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dispatchcms/react",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "React hooks and provider for Dispatch CMS",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "React hooks and provider for Dispatch CMS entries",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
"require": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsup"
|
|
17
|
-
},
|
|
18
15
|
"peerDependencies": {
|
|
19
16
|
"react": ">=18.0.0"
|
|
20
17
|
},
|
|
@@ -25,8 +22,17 @@
|
|
|
25
22
|
"typescript": "^5.7.2"
|
|
26
23
|
},
|
|
27
24
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
25
|
+
"node": ">=20.9"
|
|
29
26
|
},
|
|
30
|
-
"keywords": [
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
"keywords": [
|
|
28
|
+
"dispatch",
|
|
29
|
+
"cms",
|
|
30
|
+
"react",
|
|
31
|
+
"nextjs",
|
|
32
|
+
"content"
|
|
33
|
+
],
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/hooks.ts
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
import type {
|
|
4
|
+
import type { Entry } from "./types";
|
|
5
5
|
import { useDispatchContext } from "./context";
|
|
6
6
|
|
|
7
|
-
export type
|
|
8
|
-
|
|
7
|
+
export type UseEntryResult = {
|
|
8
|
+
entry: Entry | null;
|
|
9
9
|
isLoading: boolean;
|
|
10
10
|
error?: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export function
|
|
13
|
+
export function useEntry(collection: string | undefined, slug: string | undefined): UseEntryResult {
|
|
14
14
|
const ctx = useDispatchContext();
|
|
15
|
-
const [
|
|
15
|
+
const [entry, setEntry] = useState<Entry | null>(null);
|
|
16
16
|
const [isLoading, setIsLoading] = useState(true);
|
|
17
17
|
const [error, setError] = useState<string | undefined>(undefined);
|
|
18
18
|
|
|
19
19
|
useEffect(() => {
|
|
20
|
-
if (!slug?.trim()) {
|
|
21
|
-
|
|
20
|
+
if (!collection?.trim() || !slug?.trim()) {
|
|
21
|
+
setEntry(null);
|
|
22
22
|
setIsLoading(false);
|
|
23
23
|
setError(undefined);
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
if (!ctx?.siteKey) {
|
|
28
|
-
|
|
28
|
+
setEntry(null);
|
|
29
29
|
setIsLoading(false);
|
|
30
30
|
setError("Missing site key");
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
let cancelled = false;
|
|
35
|
-
|
|
35
|
+
setEntry(null);
|
|
36
36
|
setError(undefined);
|
|
37
37
|
setIsLoading(true);
|
|
38
38
|
|
|
39
|
-
const url = `${ctx.apiBase}/api/
|
|
39
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}/${encodeURIComponent(slug.trim())}`;
|
|
40
40
|
fetch(url, {
|
|
41
41
|
headers: { "X-Site-Key": ctx.siteKey },
|
|
42
42
|
})
|
|
43
43
|
.then((res) => {
|
|
44
44
|
if (cancelled) return;
|
|
45
45
|
if (res.status === 404) {
|
|
46
|
-
|
|
46
|
+
setEntry(null);
|
|
47
47
|
setIsLoading(false);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
if (!res.ok) {
|
|
51
|
-
|
|
51
|
+
setEntry(null);
|
|
52
52
|
setIsLoading(false);
|
|
53
53
|
setError("Failed to load");
|
|
54
54
|
return;
|
|
@@ -56,13 +56,13 @@ export function usePost(slug: string | undefined): UsePostResult {
|
|
|
56
56
|
return res.json();
|
|
57
57
|
})
|
|
58
58
|
.then((data) => {
|
|
59
|
-
if (cancelled) return;
|
|
60
|
-
|
|
59
|
+
if (cancelled || data == null) return;
|
|
60
|
+
setEntry(data as Entry);
|
|
61
61
|
setIsLoading(false);
|
|
62
62
|
})
|
|
63
63
|
.catch(() => {
|
|
64
64
|
if (cancelled) return;
|
|
65
|
-
|
|
65
|
+
setEntry(null);
|
|
66
66
|
setIsLoading(false);
|
|
67
67
|
setError("Failed to load");
|
|
68
68
|
});
|
|
@@ -70,44 +70,51 @@ export function usePost(slug: string | undefined): UsePostResult {
|
|
|
70
70
|
return () => {
|
|
71
71
|
cancelled = true;
|
|
72
72
|
};
|
|
73
|
-
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
73
|
+
}, [collection, slug, ctx?.siteKey, ctx?.apiBase]);
|
|
74
74
|
|
|
75
|
-
return {
|
|
75
|
+
return { entry, isLoading, error };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
export type
|
|
79
|
-
|
|
78
|
+
export type UseEntriesResult = {
|
|
79
|
+
entries: Entry[];
|
|
80
80
|
isLoading: boolean;
|
|
81
81
|
error?: string;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
export function
|
|
84
|
+
export function useEntries(collection: string | undefined): UseEntriesResult {
|
|
85
85
|
const ctx = useDispatchContext();
|
|
86
|
-
const [
|
|
86
|
+
const [entries, setEntries] = useState<Entry[]>([]);
|
|
87
87
|
const [isLoading, setIsLoading] = useState(true);
|
|
88
88
|
const [error, setError] = useState<string | undefined>(undefined);
|
|
89
89
|
|
|
90
90
|
useEffect(() => {
|
|
91
|
+
if (!collection?.trim()) {
|
|
92
|
+
setEntries([]);
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
setError(undefined);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
if (!ctx?.siteKey) {
|
|
92
|
-
|
|
99
|
+
setEntries([]);
|
|
93
100
|
setIsLoading(false);
|
|
94
101
|
setError("Missing site key");
|
|
95
102
|
return;
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
let cancelled = false;
|
|
99
|
-
|
|
106
|
+
setEntries([]);
|
|
100
107
|
setError(undefined);
|
|
101
108
|
setIsLoading(true);
|
|
102
109
|
|
|
103
|
-
const url = `${ctx.apiBase}/api/
|
|
110
|
+
const url = `${ctx.apiBase}/api/content/${encodeURIComponent(collection.trim())}`;
|
|
104
111
|
fetch(url, {
|
|
105
112
|
headers: { "X-Site-Key": ctx.siteKey },
|
|
106
113
|
})
|
|
107
114
|
.then((res) => {
|
|
108
115
|
if (cancelled) return;
|
|
109
116
|
if (!res.ok) {
|
|
110
|
-
|
|
117
|
+
setEntries([]);
|
|
111
118
|
setIsLoading(false);
|
|
112
119
|
setError("Failed to load");
|
|
113
120
|
return;
|
|
@@ -115,13 +122,13 @@ export function usePosts(): UsePostsResult {
|
|
|
115
122
|
return res.json();
|
|
116
123
|
})
|
|
117
124
|
.then((data) => {
|
|
118
|
-
if (cancelled) return;
|
|
119
|
-
|
|
125
|
+
if (cancelled || data == null) return;
|
|
126
|
+
setEntries(Array.isArray(data) ? data : []);
|
|
120
127
|
setIsLoading(false);
|
|
121
128
|
})
|
|
122
129
|
.catch(() => {
|
|
123
130
|
if (cancelled) return;
|
|
124
|
-
|
|
131
|
+
setEntries([]);
|
|
125
132
|
setIsLoading(false);
|
|
126
133
|
setError("Failed to load");
|
|
127
134
|
});
|
|
@@ -129,7 +136,7 @@ export function usePosts(): UsePostsResult {
|
|
|
129
136
|
return () => {
|
|
130
137
|
cancelled = true;
|
|
131
138
|
};
|
|
132
|
-
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
139
|
+
}, [collection, ctx?.siteKey, ctx?.apiBase]);
|
|
133
140
|
|
|
134
|
-
return {
|
|
141
|
+
return { entries, isLoading, error };
|
|
135
142
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { Entry } from "./types";
|
|
2
2
|
export { DispatchProvider, useDispatchContext } from "./context";
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
3
|
+
export { useEntry, useEntries } from "./hooks";
|
|
4
|
+
export type { UseEntryResult, UseEntriesResult } from "./hooks";
|
package/src/types.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type Entry = {
|
|
2
2
|
id: string;
|
|
3
3
|
created_at: string;
|
|
4
|
-
published_at: string;
|
|
4
|
+
published_at: string | null;
|
|
5
5
|
updated_at: string;
|
|
6
6
|
site_id: string;
|
|
7
|
+
collection: string;
|
|
7
8
|
title: string;
|
|
8
|
-
slug: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
featured_image: string | null;
|
|
9
|
+
slug: string | null;
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
sort_order: number;
|
|
12
12
|
published: boolean;
|
|
13
13
|
};
|