@dispatchcms/react 0.0.4
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 +23 -0
- package/dist/index.d.mts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +173 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +144 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +32 -0
- package/src/config.ts +8 -0
- package/src/context.tsx +46 -0
- package/src/hooks.ts +135 -0
- package/src/index.ts +4 -0
- package/src/types.ts +13 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @dispatchcms/react@0.0.4 build /Users/jamescalmus/Documents/dispatch/packages/react
|
|
4
|
+
> tsup
|
|
5
|
+
|
|
6
|
+
[34mCLI[39m Building entry: src/index.ts
|
|
7
|
+
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
8
|
+
[34mCLI[39m tsup v8.5.1
|
|
9
|
+
[34mCLI[39m Using tsup config: /Users/jamescalmus/Documents/dispatch/packages/react/tsup.config.ts
|
|
10
|
+
[34mCLI[39m Target: es2020
|
|
11
|
+
[34mCLI[39m Cleaning output folder
|
|
12
|
+
[34mCJS[39m Build start
|
|
13
|
+
[34mESM[39m Build start
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m3.63 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m7.27 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 6ms
|
|
17
|
+
[32mCJS[39m [1mdist/index.js [22m[32m4.94 KB[39m
|
|
18
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m7.58 KB[39m
|
|
19
|
+
[32mCJS[39m ⚡️ Build success in 6ms
|
|
20
|
+
DTS Build start
|
|
21
|
+
DTS ⚡️ Build success in 296ms
|
|
22
|
+
DTS dist/index.d.ts 1.10 KB
|
|
23
|
+
DTS dist/index.d.mts 1.10 KB
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Post = {
|
|
4
|
+
id: string;
|
|
5
|
+
created_at: string;
|
|
6
|
+
published_at: string;
|
|
7
|
+
updated_at: string;
|
|
8
|
+
site_id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
content: unknown;
|
|
12
|
+
excerpt: string | null;
|
|
13
|
+
featured_image: string | null;
|
|
14
|
+
published: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type DispatchContextValue = {
|
|
18
|
+
siteKey: string | null;
|
|
19
|
+
apiBase: string;
|
|
20
|
+
};
|
|
21
|
+
declare function useDispatchContext(): DispatchContextValue | null;
|
|
22
|
+
type DispatchProviderProps = {
|
|
23
|
+
siteKey?: string | null;
|
|
24
|
+
apiBase?: string;
|
|
25
|
+
children?: unknown;
|
|
26
|
+
};
|
|
27
|
+
declare function DispatchProvider({ siteKey, apiBase, children, }: DispatchProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
|
+
|
|
29
|
+
type UsePostResult = {
|
|
30
|
+
post: Post | null;
|
|
31
|
+
isLoading: boolean;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
34
|
+
declare function usePost(slug: string | undefined): UsePostResult;
|
|
35
|
+
type UsePostsResult = {
|
|
36
|
+
posts: Post[];
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
error?: string;
|
|
39
|
+
};
|
|
40
|
+
declare function usePosts(): UsePostsResult;
|
|
41
|
+
|
|
42
|
+
export { DispatchProvider, type Post, type UsePostResult, type UsePostsResult, useDispatchContext, usePost, usePosts };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Post = {
|
|
4
|
+
id: string;
|
|
5
|
+
created_at: string;
|
|
6
|
+
published_at: string;
|
|
7
|
+
updated_at: string;
|
|
8
|
+
site_id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
content: unknown;
|
|
12
|
+
excerpt: string | null;
|
|
13
|
+
featured_image: string | null;
|
|
14
|
+
published: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type DispatchContextValue = {
|
|
18
|
+
siteKey: string | null;
|
|
19
|
+
apiBase: string;
|
|
20
|
+
};
|
|
21
|
+
declare function useDispatchContext(): DispatchContextValue | null;
|
|
22
|
+
type DispatchProviderProps = {
|
|
23
|
+
siteKey?: string | null;
|
|
24
|
+
apiBase?: string;
|
|
25
|
+
children?: unknown;
|
|
26
|
+
};
|
|
27
|
+
declare function DispatchProvider({ siteKey, apiBase, children, }: DispatchProviderProps): react_jsx_runtime.JSX.Element;
|
|
28
|
+
|
|
29
|
+
type UsePostResult = {
|
|
30
|
+
post: Post | null;
|
|
31
|
+
isLoading: boolean;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
34
|
+
declare function usePost(slug: string | undefined): UsePostResult;
|
|
35
|
+
type UsePostsResult = {
|
|
36
|
+
posts: Post[];
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
error?: string;
|
|
39
|
+
};
|
|
40
|
+
declare function usePosts(): UsePostsResult;
|
|
41
|
+
|
|
42
|
+
export { DispatchProvider, type Post, type UsePostResult, type UsePostsResult, useDispatchContext, usePost, usePosts };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
DispatchProvider: () => DispatchProvider,
|
|
25
|
+
useDispatchContext: () => useDispatchContext,
|
|
26
|
+
usePost: () => usePost,
|
|
27
|
+
usePosts: () => usePosts
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/context.tsx
|
|
32
|
+
var import_react = require("react");
|
|
33
|
+
|
|
34
|
+
// src/config.ts
|
|
35
|
+
var API_BASE = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DISPATCH_API_URL ? process.env.NEXT_PUBLIC_DISPATCH_API_URL : "https://dispatch-cms.vercel.app";
|
|
36
|
+
function getApiBase() {
|
|
37
|
+
return API_BASE;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/context.tsx
|
|
41
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
42
|
+
var DispatchContext = (0, import_react.createContext)(null);
|
|
43
|
+
function useDispatchContext() {
|
|
44
|
+
return (0, import_react.useContext)(DispatchContext);
|
|
45
|
+
}
|
|
46
|
+
function DispatchProvider({
|
|
47
|
+
siteKey = null,
|
|
48
|
+
apiBase = getApiBase(),
|
|
49
|
+
children
|
|
50
|
+
}) {
|
|
51
|
+
const value = {
|
|
52
|
+
siteKey: siteKey?.trim() || null,
|
|
53
|
+
apiBase
|
|
54
|
+
};
|
|
55
|
+
if (!value.siteKey) {
|
|
56
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
57
|
+
console.warn(
|
|
58
|
+
"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider or set NEXT_PUBLIC_DISPATCH_SITE_KEY. Hooks will return empty/error state."
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DispatchContext.Provider, { value, children });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/hooks.ts
|
|
66
|
+
var import_react2 = require("react");
|
|
67
|
+
function usePost(slug) {
|
|
68
|
+
const ctx = useDispatchContext();
|
|
69
|
+
const [post, setPost] = (0, import_react2.useState)(null);
|
|
70
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
71
|
+
const [error, setError] = (0, import_react2.useState)(void 0);
|
|
72
|
+
(0, import_react2.useEffect)(() => {
|
|
73
|
+
if (!slug?.trim()) {
|
|
74
|
+
setPost(null);
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
setError(void 0);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!ctx?.siteKey) {
|
|
80
|
+
setPost(null);
|
|
81
|
+
setIsLoading(false);
|
|
82
|
+
setError("Missing site key");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let cancelled = false;
|
|
86
|
+
setPost(null);
|
|
87
|
+
setError(void 0);
|
|
88
|
+
setIsLoading(true);
|
|
89
|
+
const url = `${ctx.apiBase}/api/posts/${encodeURIComponent(slug.trim())}`;
|
|
90
|
+
fetch(url, {
|
|
91
|
+
headers: { "X-Site-Key": ctx.siteKey }
|
|
92
|
+
}).then((res) => {
|
|
93
|
+
if (cancelled) return;
|
|
94
|
+
if (res.status === 404) {
|
|
95
|
+
setPost(null);
|
|
96
|
+
setIsLoading(false);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
setPost(null);
|
|
101
|
+
setIsLoading(false);
|
|
102
|
+
setError("Failed to load");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
return res.json();
|
|
106
|
+
}).then((data) => {
|
|
107
|
+
if (cancelled) return;
|
|
108
|
+
setPost(data);
|
|
109
|
+
setIsLoading(false);
|
|
110
|
+
}).catch(() => {
|
|
111
|
+
if (cancelled) return;
|
|
112
|
+
setPost(null);
|
|
113
|
+
setIsLoading(false);
|
|
114
|
+
setError("Failed to load");
|
|
115
|
+
});
|
|
116
|
+
return () => {
|
|
117
|
+
cancelled = true;
|
|
118
|
+
};
|
|
119
|
+
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
120
|
+
return { post, isLoading, error };
|
|
121
|
+
}
|
|
122
|
+
function usePosts() {
|
|
123
|
+
const ctx = useDispatchContext();
|
|
124
|
+
const [posts, setPosts] = (0, import_react2.useState)([]);
|
|
125
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
126
|
+
const [error, setError] = (0, import_react2.useState)(void 0);
|
|
127
|
+
(0, import_react2.useEffect)(() => {
|
|
128
|
+
if (!ctx?.siteKey) {
|
|
129
|
+
setPosts([]);
|
|
130
|
+
setIsLoading(false);
|
|
131
|
+
setError("Missing site key");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let cancelled = false;
|
|
135
|
+
setPosts([]);
|
|
136
|
+
setError(void 0);
|
|
137
|
+
setIsLoading(true);
|
|
138
|
+
const url = `${ctx.apiBase}/api/posts`;
|
|
139
|
+
fetch(url, {
|
|
140
|
+
headers: { "X-Site-Key": ctx.siteKey }
|
|
141
|
+
}).then((res) => {
|
|
142
|
+
if (cancelled) return;
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
setPosts([]);
|
|
145
|
+
setIsLoading(false);
|
|
146
|
+
setError("Failed to load");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
return res.json();
|
|
150
|
+
}).then((data) => {
|
|
151
|
+
if (cancelled) return;
|
|
152
|
+
setPosts(Array.isArray(data) ? data : []);
|
|
153
|
+
setIsLoading(false);
|
|
154
|
+
}).catch(() => {
|
|
155
|
+
if (cancelled) return;
|
|
156
|
+
setPosts([]);
|
|
157
|
+
setIsLoading(false);
|
|
158
|
+
setError("Failed to load");
|
|
159
|
+
});
|
|
160
|
+
return () => {
|
|
161
|
+
cancelled = true;
|
|
162
|
+
};
|
|
163
|
+
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
164
|
+
return { posts, isLoading, error };
|
|
165
|
+
}
|
|
166
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
167
|
+
0 && (module.exports = {
|
|
168
|
+
DispatchProvider,
|
|
169
|
+
useDispatchContext,
|
|
170
|
+
usePost,
|
|
171
|
+
usePosts
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/config.ts","../src/hooks.ts"],"sourcesContent":["export type { Post } from \"./types\";\nexport { DispatchProvider, useDispatchContext } from \"./context\";\nexport { usePost, usePosts } from \"./hooks\";\nexport type { UsePostResult, UsePostsResult } 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 or set NEXT_PUBLIC_DISPATCH_SITE_KEY. 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 API_BASE =\n typeof process !== \"undefined\" && process.env?.NEXT_PUBLIC_DISPATCH_API_URL\n ? process.env.NEXT_PUBLIC_DISPATCH_API_URL\n : \"https://dispatch-cms.vercel.app\";\n\nexport function getApiBase(): string {\n return API_BASE;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { Post } from \"./types\";\nimport { useDispatchContext } from \"./context\";\n\nexport type UsePostResult = {\n post: Post | null;\n isLoading: boolean;\n error?: string;\n};\n\nexport function usePost(slug: string | undefined): UsePostResult {\n const ctx = useDispatchContext();\n const [post, setPost] = useState<Post | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!slug?.trim()) {\n setPost(null);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setPost(null);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setPost(null);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/posts/${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 setPost(null);\n setIsLoading(false);\n return;\n }\n if (!res.ok) {\n setPost(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled) return;\n setPost(data as Post);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setPost(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [slug, ctx?.siteKey, ctx?.apiBase]);\n\n return { post, isLoading, error };\n}\n\nexport type UsePostsResult = {\n posts: Post[];\n isLoading: boolean;\n error?: string;\n};\n\nexport function usePosts(): UsePostsResult {\n const ctx = useDispatchContext();\n const [posts, setPosts] = useState<Post[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!ctx?.siteKey) {\n setPosts([]);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setPosts([]);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/posts`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (!res.ok) {\n setPosts([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled) return;\n setPosts(Array.isArray(data) ? data : []);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setPosts([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [ctx?.siteKey, ctx?.apiBase]);\n\n return { posts, isLoading, error };\n}\n"],"mappings":"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","names":["import_react"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// src/context.tsx
|
|
4
|
+
import { createContext, useContext } from "react";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
var API_BASE = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DISPATCH_API_URL ? process.env.NEXT_PUBLIC_DISPATCH_API_URL : "https://dispatch-cms.vercel.app";
|
|
8
|
+
function getApiBase() {
|
|
9
|
+
return API_BASE;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/context.tsx
|
|
13
|
+
import { jsx } from "react/jsx-runtime";
|
|
14
|
+
var DispatchContext = createContext(null);
|
|
15
|
+
function useDispatchContext() {
|
|
16
|
+
return useContext(DispatchContext);
|
|
17
|
+
}
|
|
18
|
+
function DispatchProvider({
|
|
19
|
+
siteKey = null,
|
|
20
|
+
apiBase = getApiBase(),
|
|
21
|
+
children
|
|
22
|
+
}) {
|
|
23
|
+
const value = {
|
|
24
|
+
siteKey: siteKey?.trim() || null,
|
|
25
|
+
apiBase
|
|
26
|
+
};
|
|
27
|
+
if (!value.siteKey) {
|
|
28
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
29
|
+
console.warn(
|
|
30
|
+
"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider or set NEXT_PUBLIC_DISPATCH_SITE_KEY. Hooks will return empty/error state."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsx(DispatchContext.Provider, { value, children });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/hooks.ts
|
|
38
|
+
import { useEffect, useState } from "react";
|
|
39
|
+
function usePost(slug) {
|
|
40
|
+
const ctx = useDispatchContext();
|
|
41
|
+
const [post, setPost] = useState(null);
|
|
42
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
43
|
+
const [error, setError] = useState(void 0);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!slug?.trim()) {
|
|
46
|
+
setPost(null);
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
setError(void 0);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!ctx?.siteKey) {
|
|
52
|
+
setPost(null);
|
|
53
|
+
setIsLoading(false);
|
|
54
|
+
setError("Missing site key");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let cancelled = false;
|
|
58
|
+
setPost(null);
|
|
59
|
+
setError(void 0);
|
|
60
|
+
setIsLoading(true);
|
|
61
|
+
const url = `${ctx.apiBase}/api/posts/${encodeURIComponent(slug.trim())}`;
|
|
62
|
+
fetch(url, {
|
|
63
|
+
headers: { "X-Site-Key": ctx.siteKey }
|
|
64
|
+
}).then((res) => {
|
|
65
|
+
if (cancelled) return;
|
|
66
|
+
if (res.status === 404) {
|
|
67
|
+
setPost(null);
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
setPost(null);
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
setError("Failed to load");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
return res.json();
|
|
78
|
+
}).then((data) => {
|
|
79
|
+
if (cancelled) return;
|
|
80
|
+
setPost(data);
|
|
81
|
+
setIsLoading(false);
|
|
82
|
+
}).catch(() => {
|
|
83
|
+
if (cancelled) return;
|
|
84
|
+
setPost(null);
|
|
85
|
+
setIsLoading(false);
|
|
86
|
+
setError("Failed to load");
|
|
87
|
+
});
|
|
88
|
+
return () => {
|
|
89
|
+
cancelled = true;
|
|
90
|
+
};
|
|
91
|
+
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
92
|
+
return { post, isLoading, error };
|
|
93
|
+
}
|
|
94
|
+
function usePosts() {
|
|
95
|
+
const ctx = useDispatchContext();
|
|
96
|
+
const [posts, setPosts] = useState([]);
|
|
97
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
98
|
+
const [error, setError] = useState(void 0);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!ctx?.siteKey) {
|
|
101
|
+
setPosts([]);
|
|
102
|
+
setIsLoading(false);
|
|
103
|
+
setError("Missing site key");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let cancelled = false;
|
|
107
|
+
setPosts([]);
|
|
108
|
+
setError(void 0);
|
|
109
|
+
setIsLoading(true);
|
|
110
|
+
const url = `${ctx.apiBase}/api/posts`;
|
|
111
|
+
fetch(url, {
|
|
112
|
+
headers: { "X-Site-Key": ctx.siteKey }
|
|
113
|
+
}).then((res) => {
|
|
114
|
+
if (cancelled) return;
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
setPosts([]);
|
|
117
|
+
setIsLoading(false);
|
|
118
|
+
setError("Failed to load");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
return res.json();
|
|
122
|
+
}).then((data) => {
|
|
123
|
+
if (cancelled) return;
|
|
124
|
+
setPosts(Array.isArray(data) ? data : []);
|
|
125
|
+
setIsLoading(false);
|
|
126
|
+
}).catch(() => {
|
|
127
|
+
if (cancelled) return;
|
|
128
|
+
setPosts([]);
|
|
129
|
+
setIsLoading(false);
|
|
130
|
+
setError("Failed to load");
|
|
131
|
+
});
|
|
132
|
+
return () => {
|
|
133
|
+
cancelled = true;
|
|
134
|
+
};
|
|
135
|
+
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
136
|
+
return { posts, isLoading, error };
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
DispatchProvider,
|
|
140
|
+
useDispatchContext,
|
|
141
|
+
usePost,
|
|
142
|
+
usePosts
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 or set NEXT_PUBLIC_DISPATCH_SITE_KEY. 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 API_BASE =\n typeof process !== \"undefined\" && process.env?.NEXT_PUBLIC_DISPATCH_API_URL\n ? process.env.NEXT_PUBLIC_DISPATCH_API_URL\n : \"https://dispatch-cms.vercel.app\";\n\nexport function getApiBase(): string {\n return API_BASE;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { Post } from \"./types\";\nimport { useDispatchContext } from \"./context\";\n\nexport type UsePostResult = {\n post: Post | null;\n isLoading: boolean;\n error?: string;\n};\n\nexport function usePost(slug: string | undefined): UsePostResult {\n const ctx = useDispatchContext();\n const [post, setPost] = useState<Post | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!slug?.trim()) {\n setPost(null);\n setIsLoading(false);\n setError(undefined);\n return;\n }\n\n if (!ctx?.siteKey) {\n setPost(null);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setPost(null);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/posts/${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 setPost(null);\n setIsLoading(false);\n return;\n }\n if (!res.ok) {\n setPost(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled) return;\n setPost(data as Post);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setPost(null);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [slug, ctx?.siteKey, ctx?.apiBase]);\n\n return { post, isLoading, error };\n}\n\nexport type UsePostsResult = {\n posts: Post[];\n isLoading: boolean;\n error?: string;\n};\n\nexport function usePosts(): UsePostsResult {\n const ctx = useDispatchContext();\n const [posts, setPosts] = useState<Post[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!ctx?.siteKey) {\n setPosts([]);\n setIsLoading(false);\n setError(\"Missing site key\");\n return;\n }\n\n let cancelled = false;\n setPosts([]);\n setError(undefined);\n setIsLoading(true);\n\n const url = `${ctx.apiBase}/api/posts`;\n fetch(url, {\n headers: { \"X-Site-Key\": ctx.siteKey },\n })\n .then((res) => {\n if (cancelled) return;\n if (!res.ok) {\n setPosts([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n return;\n }\n return res.json();\n })\n .then((data) => {\n if (cancelled) return;\n setPosts(Array.isArray(data) ? data : []);\n setIsLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setPosts([]);\n setIsLoading(false);\n setError(\"Failed to load\");\n });\n\n return () => {\n cancelled = true;\n };\n }, [ctx?.siteKey, ctx?.apiBase]);\n\n return { posts, isLoading, error };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dispatchcms/react",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "React hooks and provider for Dispatch CMS",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/react": "^18.3.12",
|
|
23
|
+
"react": "^18.3.1",
|
|
24
|
+
"tsup": "^8.3.5",
|
|
25
|
+
"typescript": "^5.7.2"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"keywords": ["dispatch", "cms", "react", "nextjs", "content"],
|
|
31
|
+
"license": "MIT"
|
|
32
|
+
}
|
package/src/config.ts
ADDED
package/src/context.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext } from "react";
|
|
4
|
+
import { getApiBase } from "./config";
|
|
5
|
+
|
|
6
|
+
type DispatchContextValue = {
|
|
7
|
+
siteKey: string | null;
|
|
8
|
+
apiBase: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DispatchContext = createContext<DispatchContextValue | null>(null);
|
|
12
|
+
|
|
13
|
+
export function useDispatchContext(): DispatchContextValue | null {
|
|
14
|
+
return useContext(DispatchContext);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type DispatchProviderProps = {
|
|
18
|
+
siteKey?: string | null;
|
|
19
|
+
apiBase?: string;
|
|
20
|
+
children?: unknown;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function DispatchProvider({
|
|
24
|
+
siteKey = null,
|
|
25
|
+
apiBase = getApiBase(),
|
|
26
|
+
children,
|
|
27
|
+
}: DispatchProviderProps) {
|
|
28
|
+
const value: DispatchContextValue = {
|
|
29
|
+
siteKey: siteKey?.trim() || null,
|
|
30
|
+
apiBase,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (!value.siteKey) {
|
|
34
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
35
|
+
console.warn(
|
|
36
|
+
"[Dispatch] No siteKey provided. Pass siteKey to DispatchProvider or set NEXT_PUBLIC_DISPATCH_SITE_KEY. Hooks will return empty/error state."
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<DispatchContext.Provider value={value}>
|
|
43
|
+
{children as React.ReactNode}
|
|
44
|
+
</DispatchContext.Provider>
|
|
45
|
+
);
|
|
46
|
+
}
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import type { Post } from "./types";
|
|
5
|
+
import { useDispatchContext } from "./context";
|
|
6
|
+
|
|
7
|
+
export type UsePostResult = {
|
|
8
|
+
post: Post | null;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function usePost(slug: string | undefined): UsePostResult {
|
|
14
|
+
const ctx = useDispatchContext();
|
|
15
|
+
const [post, setPost] = useState<Post | null>(null);
|
|
16
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
17
|
+
const [error, setError] = useState<string | undefined>(undefined);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!slug?.trim()) {
|
|
21
|
+
setPost(null);
|
|
22
|
+
setIsLoading(false);
|
|
23
|
+
setError(undefined);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!ctx?.siteKey) {
|
|
28
|
+
setPost(null);
|
|
29
|
+
setIsLoading(false);
|
|
30
|
+
setError("Missing site key");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
setPost(null);
|
|
36
|
+
setError(undefined);
|
|
37
|
+
setIsLoading(true);
|
|
38
|
+
|
|
39
|
+
const url = `${ctx.apiBase}/api/posts/${encodeURIComponent(slug.trim())}`;
|
|
40
|
+
fetch(url, {
|
|
41
|
+
headers: { "X-Site-Key": ctx.siteKey },
|
|
42
|
+
})
|
|
43
|
+
.then((res) => {
|
|
44
|
+
if (cancelled) return;
|
|
45
|
+
if (res.status === 404) {
|
|
46
|
+
setPost(null);
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
setPost(null);
|
|
52
|
+
setIsLoading(false);
|
|
53
|
+
setError("Failed to load");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
return res.json();
|
|
57
|
+
})
|
|
58
|
+
.then((data) => {
|
|
59
|
+
if (cancelled) return;
|
|
60
|
+
setPost(data as Post);
|
|
61
|
+
setIsLoading(false);
|
|
62
|
+
})
|
|
63
|
+
.catch(() => {
|
|
64
|
+
if (cancelled) return;
|
|
65
|
+
setPost(null);
|
|
66
|
+
setIsLoading(false);
|
|
67
|
+
setError("Failed to load");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
cancelled = true;
|
|
72
|
+
};
|
|
73
|
+
}, [slug, ctx?.siteKey, ctx?.apiBase]);
|
|
74
|
+
|
|
75
|
+
return { post, isLoading, error };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type UsePostsResult = {
|
|
79
|
+
posts: Post[];
|
|
80
|
+
isLoading: boolean;
|
|
81
|
+
error?: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export function usePosts(): UsePostsResult {
|
|
85
|
+
const ctx = useDispatchContext();
|
|
86
|
+
const [posts, setPosts] = useState<Post[]>([]);
|
|
87
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
88
|
+
const [error, setError] = useState<string | undefined>(undefined);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!ctx?.siteKey) {
|
|
92
|
+
setPosts([]);
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
setError("Missing site key");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let cancelled = false;
|
|
99
|
+
setPosts([]);
|
|
100
|
+
setError(undefined);
|
|
101
|
+
setIsLoading(true);
|
|
102
|
+
|
|
103
|
+
const url = `${ctx.apiBase}/api/posts`;
|
|
104
|
+
fetch(url, {
|
|
105
|
+
headers: { "X-Site-Key": ctx.siteKey },
|
|
106
|
+
})
|
|
107
|
+
.then((res) => {
|
|
108
|
+
if (cancelled) return;
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
setPosts([]);
|
|
111
|
+
setIsLoading(false);
|
|
112
|
+
setError("Failed to load");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
return res.json();
|
|
116
|
+
})
|
|
117
|
+
.then((data) => {
|
|
118
|
+
if (cancelled) return;
|
|
119
|
+
setPosts(Array.isArray(data) ? data : []);
|
|
120
|
+
setIsLoading(false);
|
|
121
|
+
})
|
|
122
|
+
.catch(() => {
|
|
123
|
+
if (cancelled) return;
|
|
124
|
+
setPosts([]);
|
|
125
|
+
setIsLoading(false);
|
|
126
|
+
setError("Failed to load");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return () => {
|
|
130
|
+
cancelled = true;
|
|
131
|
+
};
|
|
132
|
+
}, [ctx?.siteKey, ctx?.apiBase]);
|
|
133
|
+
|
|
134
|
+
return { posts, isLoading, error };
|
|
135
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Post = {
|
|
2
|
+
id: string;
|
|
3
|
+
created_at: string;
|
|
4
|
+
published_at: string;
|
|
5
|
+
updated_at: string;
|
|
6
|
+
site_id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
content: unknown;
|
|
10
|
+
excerpt: string | null;
|
|
11
|
+
featured_image: string | null;
|
|
12
|
+
published: boolean;
|
|
13
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"declarationMap": true,
|
|
12
|
+
"outDir": "dist"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
|
+
|
|
3
|
+
const useClientBanner = { js: "'use client';", css: "" };
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
entry: ["src/index.ts"],
|
|
7
|
+
format: ["cjs", "esm"],
|
|
8
|
+
dts: true,
|
|
9
|
+
clean: true,
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
external: ["react"],
|
|
12
|
+
banner: useClientBanner,
|
|
13
|
+
});
|