@seriphxyz/astro 0.1.2 → 0.1.5

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/src/index.ts CHANGED
@@ -1,212 +1,71 @@
1
- const DEFAULT_ENDPOINT = "https://seriph.xyz";
2
- const API_PATH = "/api/v1";
3
-
4
- // Types
5
- export interface SeriphConfig {
6
- /** Your site key (required) */
7
- siteKey: string;
8
- /** @deprecated Use siteKey instead */
9
- apiKey?: string;
10
- /** Base URL of your Seriph instance (default: 'https://seriph.xyz') */
11
- endpoint?: string;
12
- }
1
+ /**
2
+ * @seriphxyz/astro
3
+ *
4
+ * Astro components and content loader for Seriph widgets.
5
+ * Re-exports all types, API functions, and controllers from @seriphxyz/core.
6
+ */
7
+
8
+ // Re-export everything from core
9
+ export {
10
+ // Constants
11
+ DEFAULT_ENDPOINT,
12
+ API_PATH,
13
+
14
+ // Types
15
+ type SeriphConfig,
16
+ type Comment,
17
+ type ReactionCounts,
18
+ type FormSubmitResponse,
19
+ type SubscribeResponse,
20
+ type SeriphPost,
13
21
 
14
- /** @deprecated Use SeriphConfig instead */
15
- export type SeraphConfig = SeriphConfig;
22
+ // Helpers
23
+ buildUrl,
24
+ getSiteKey,
25
+
26
+ // API Functions - Forms
27
+ type SubmitFormOptions,
28
+ submitForm,
29
+
30
+ // API Functions - Comments
31
+ type FetchCommentsOptions,
32
+ fetchComments,
33
+ type PostCommentOptions,
34
+ postComment,
35
+
36
+ // API Functions - Reactions
37
+ type FetchReactionsOptions,
38
+ fetchReactions,
39
+ type AddReactionOptions,
40
+ addReaction,
41
+ type RemoveReactionOptions,
42
+ removeReaction,
43
+
44
+ // API Functions - Subscriptions
45
+ type SubscribeOptions,
46
+ subscribe,
47
+
48
+ // API Functions - Posts
49
+ type FetchPostsOptions,
50
+ fetchPosts,
51
+ type FetchPostOptions,
52
+ fetchPost,
16
53
 
17
- // Re-export loader
54
+ // Controllers
55
+ type ControllerStatus,
56
+ type ControllerListener,
57
+ type SubscribeState,
58
+ type FormState,
59
+ type ReactionsState,
60
+ type CommentsState,
61
+ SubscribeController,
62
+ FormController,
63
+ ReactionsController,
64
+ CommentsController,
65
+ } from "@seriphxyz/core";
66
+
67
+ // Re-export loader (Astro-specific)
18
68
  export {
19
69
  seriphPostsLoader,
20
- seraphPostsLoader,
21
- fetchPosts,
22
- fetchPost,
23
- type SeriphPost,
24
- type SeraphPost,
25
70
  type SeriphPostsLoaderOptions,
26
- type SeraphPostsLoaderOptions,
27
- type FetchPostsOptions,
28
- type FetchPostOptions,
29
71
  } from "./loader.js";
30
-
31
- export interface FormSubmitOptions {
32
- onSuccess?: (data: unknown) => void;
33
- onError?: (error: Error) => void;
34
- }
35
-
36
- export interface Comment {
37
- id: string;
38
- pageId: string;
39
- parentId?: string;
40
- authorName: string;
41
- content: string;
42
- createdAt: string;
43
- replies: Comment[];
44
- }
45
-
46
- export interface ReactionCounts {
47
- pageId: string;
48
- counts: Record<string, number>;
49
- }
50
-
51
- // Helper to build API URL
52
- function buildUrl(endpoint: string | undefined, path: string): string {
53
- const base = (endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, "");
54
- return `${base}${API_PATH}${path}`;
55
- }
56
-
57
- // Helper to get site key (supports both siteKey and deprecated apiKey)
58
- function getSiteKey(config: SeriphConfig): string {
59
- const key = config.siteKey || config.apiKey;
60
- if (!key) {
61
- throw new Error("siteKey is required");
62
- }
63
- return key;
64
- }
65
-
66
- export interface SubmitFormOptions extends SeriphConfig {
67
- formSlug: string;
68
- data: Record<string, unknown>;
69
- /** Form load timestamp for spam detection (auto-set if not provided) */
70
- formLoadTime?: number;
71
- }
72
-
73
- export interface FormSubmitResponse {
74
- success: boolean;
75
- message: string;
76
- }
77
-
78
- // Client utilities
79
- export async function submitForm(options: SubmitFormOptions): Promise<FormSubmitResponse> {
80
- const { endpoint, formSlug, data, formLoadTime } = options;
81
- const siteKey = getSiteKey(options);
82
- const url = buildUrl(endpoint, `/forms/${formSlug}/submit`);
83
-
84
- // Include timestamp for time-based spam detection
85
- const payload = {
86
- ...data,
87
- _seriph_ts: formLoadTime || Math.floor(Date.now() / 1000),
88
- };
89
-
90
- const response = await fetch(url, {
91
- method: "POST",
92
- headers: {
93
- "Content-Type": "application/json",
94
- "X-Seriph-Key": siteKey,
95
- },
96
- body: JSON.stringify(payload),
97
- });
98
-
99
- if (!response.ok) {
100
- throw new Error(`Form submission failed: ${response.statusText}`);
101
- }
102
-
103
- return response.json();
104
- }
105
-
106
- export interface FetchCommentsOptions extends SeriphConfig {
107
- pageId: string;
108
- }
109
-
110
- export async function fetchComments(options: FetchCommentsOptions): Promise<Comment[]> {
111
- const { endpoint, pageId } = options;
112
- const siteKey = getSiteKey(options);
113
- const url = buildUrl(endpoint, `/comments/${encodeURIComponent(pageId)}`);
114
-
115
- const response = await fetch(url, {
116
- headers: {
117
- "X-Seriph-Key": siteKey,
118
- },
119
- });
120
-
121
- if (!response.ok) {
122
- throw new Error(`Failed to fetch comments: ${response.statusText}`);
123
- }
124
-
125
- const data = await response.json();
126
- return data.data || [];
127
- }
128
-
129
- export interface PostCommentOptions extends SeriphConfig {
130
- pageId: string;
131
- authorName: string;
132
- authorEmail?: string;
133
- content: string;
134
- parentId?: string;
135
- }
136
-
137
- export async function postComment(options: PostCommentOptions): Promise<Comment> {
138
- const { endpoint, pageId, ...rest } = options;
139
- const siteKey = getSiteKey(options);
140
- const url = buildUrl(endpoint, `/comments/${encodeURIComponent(pageId)}`);
141
-
142
- const response = await fetch(url, {
143
- method: "POST",
144
- headers: {
145
- "Content-Type": "application/json",
146
- "X-Seriph-Key": siteKey,
147
- },
148
- body: JSON.stringify({
149
- authorName: rest.authorName,
150
- authorEmail: rest.authorEmail,
151
- content: rest.content,
152
- parentId: rest.parentId,
153
- }),
154
- });
155
-
156
- if (!response.ok) {
157
- throw new Error(`Failed to post comment: ${response.statusText}`);
158
- }
159
-
160
- const data = await response.json();
161
- return data.data;
162
- }
163
-
164
- export interface FetchReactionsOptions extends SeriphConfig {
165
- pageId: string;
166
- }
167
-
168
- export async function fetchReactions(options: FetchReactionsOptions): Promise<ReactionCounts> {
169
- const { endpoint, pageId } = options;
170
- const siteKey = getSiteKey(options);
171
- const url = buildUrl(endpoint, `/reactions/${encodeURIComponent(pageId)}`);
172
-
173
- const response = await fetch(url, {
174
- headers: {
175
- "X-Seriph-Key": siteKey,
176
- },
177
- });
178
-
179
- if (!response.ok) {
180
- throw new Error(`Failed to fetch reactions: ${response.statusText}`);
181
- }
182
-
183
- const data = await response.json();
184
- return data.data;
185
- }
186
-
187
- export interface AddReactionOptions extends SeriphConfig {
188
- pageId: string;
189
- reactionType?: string;
190
- }
191
-
192
- export async function addReaction(options: AddReactionOptions): Promise<{ reactionType: string; count: number }> {
193
- const { endpoint, pageId, reactionType = "like" } = options;
194
- const siteKey = getSiteKey(options);
195
- const url = buildUrl(endpoint, `/reactions/${encodeURIComponent(pageId)}`);
196
-
197
- const response = await fetch(url, {
198
- method: "POST",
199
- headers: {
200
- "Content-Type": "application/json",
201
- "X-Seriph-Key": siteKey,
202
- },
203
- body: JSON.stringify({ reactionType }),
204
- });
205
-
206
- if (!response.ok) {
207
- throw new Error(`Failed to add reaction: ${response.statusText}`);
208
- }
209
-
210
- const data = await response.json();
211
- return data.data;
212
- }
package/src/loader.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * @example
7
7
  * // In src/content.config.ts
8
8
  * import { defineCollection } from 'astro:content';
9
- * import { seriphPostsLoader } from 'seriph-astro/loader';
9
+ * import { seriphPostsLoader } from '@seriphxyz/astro/loader';
10
10
  *
11
11
  * const posts = defineCollection({
12
12
  * loader: seriphPostsLoader({
@@ -17,30 +17,24 @@
17
17
  * export const collections = { posts };
18
18
  */
19
19
 
20
- const DEFAULT_ENDPOINT = "https://seriph.xyz";
21
- const API_PATH = "/api/v1";
22
-
23
- export interface SeriphPost {
24
- id: string;
25
- title: string;
26
- slug: string;
27
- content: string;
28
- excerpt?: string;
29
- coverImage?: string;
30
- metaTitle?: string;
31
- metaDescription?: string;
32
- tags: string[];
33
- publishedAt: string;
34
- }
35
-
36
- /** @deprecated Use SeriphPost instead */
37
- export type SeraphPost = SeriphPost;
20
+ import {
21
+ DEFAULT_ENDPOINT,
22
+ API_PATH,
23
+ getSiteKey,
24
+ fetchPosts as coreFetchPosts,
25
+ fetchPost as coreFetchPost,
26
+ type SeriphPost,
27
+ type FetchPostsOptions,
28
+ type FetchPostOptions,
29
+ } from "@seriphxyz/core";
30
+
31
+ // Re-export types and functions from core
32
+ export type { SeriphPost, FetchPostsOptions, FetchPostOptions };
33
+ export { coreFetchPosts as fetchPosts, coreFetchPost as fetchPost };
38
34
 
39
35
  export interface SeriphPostsLoaderOptions {
40
36
  /** Your site key (required) */
41
- siteKey?: string;
42
- /** @deprecated Use siteKey instead */
43
- apiKey?: string;
37
+ siteKey: string;
44
38
  /** Base URL of your Seriph instance (default: 'https://seriph.xyz') */
45
39
  endpoint?: string;
46
40
  /** Filter posts by tag */
@@ -51,9 +45,6 @@ export interface SeriphPostsLoaderOptions {
51
45
  onError?: "throw" | "warn" | "ignore";
52
46
  }
53
47
 
54
- /** @deprecated Use SeriphPostsLoaderOptions instead */
55
- export type SeraphPostsLoaderOptions = SeriphPostsLoaderOptions;
56
-
57
48
  interface LoaderContext {
58
49
  store: {
59
50
  set: (entry: { id: string; data: SeriphPost }) => void;
@@ -68,30 +59,10 @@ interface LoaderContext {
68
59
  }
69
60
 
70
61
  interface ApiResponse {
71
- posts: Array<{
72
- id: string;
73
- title: string;
74
- slug: string;
75
- content: string;
76
- excerpt?: string;
77
- coverImage?: string;
78
- metaTitle?: string;
79
- metaDescription?: string;
80
- tags: string[];
81
- publishedAt: string;
82
- }>;
62
+ posts: SeriphPost[];
83
63
  total: number;
84
64
  }
85
65
 
86
- // Helper to get site key (supports both siteKey and deprecated apiKey)
87
- function getSiteKey(options: SeriphPostsLoaderOptions): string {
88
- const key = options.siteKey || options.apiKey;
89
- if (!key) {
90
- throw new Error("siteKey is required");
91
- }
92
- return key;
93
- }
94
-
95
66
  /**
96
67
  * Creates an Astro content loader that fetches posts from Seriph.
97
68
  *
@@ -105,9 +76,7 @@ export function seriphPostsLoader(options: SeriphPostsLoaderOptions) {
105
76
  onError = "throw",
106
77
  } = options;
107
78
 
108
- const siteKey = getSiteKey(options);
109
-
110
- // Build the base API URL (strip trailing slash, add API path)
79
+ const siteKey = getSiteKey({ siteKey: options.siteKey });
111
80
  const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
112
81
 
113
82
  return {
@@ -117,7 +86,6 @@ export function seriphPostsLoader(options: SeriphPostsLoaderOptions) {
117
86
  const { store, logger } = context;
118
87
 
119
88
  try {
120
- // Build the URL with query parameters
121
89
  const url = new URL(`${baseUrl}/posts`);
122
90
  url.searchParams.set("limit", String(limit));
123
91
  if (tag) {
@@ -140,25 +108,12 @@ export function seriphPostsLoader(options: SeriphPostsLoaderOptions) {
140
108
 
141
109
  const data: ApiResponse = await response.json();
142
110
 
143
- // Clear previous entries
144
111
  store.clear();
145
112
 
146
- // Add each post as an entry
147
113
  for (const post of data.posts) {
148
114
  store.set({
149
115
  id: post.slug,
150
- data: {
151
- id: post.id,
152
- title: post.title,
153
- slug: post.slug,
154
- content: post.content,
155
- excerpt: post.excerpt,
156
- coverImage: post.coverImage,
157
- metaTitle: post.metaTitle,
158
- metaDescription: post.metaDescription,
159
- tags: post.tags,
160
- publishedAt: post.publishedAt,
161
- },
116
+ data: post,
162
117
  });
163
118
  }
164
119
 
@@ -172,93 +127,7 @@ export function seriphPostsLoader(options: SeriphPostsLoaderOptions) {
172
127
  } else if (onError === "warn") {
173
128
  logger.warn(`Error loading posts (continuing anyway): ${message}`);
174
129
  }
175
- // onError === "ignore" - silently continue
176
130
  }
177
131
  },
178
132
  };
179
133
  }
180
-
181
- /** @deprecated Use seriphPostsLoader instead (note the 'i' in seriph) */
182
- export const seraphPostsLoader = seriphPostsLoader;
183
-
184
- export interface FetchPostsOptions {
185
- /** Your site key (required) */
186
- siteKey?: string;
187
- /** @deprecated Use siteKey instead */
188
- apiKey?: string;
189
- /** Base URL of your Seriph instance (default: 'https://seriph.xyz') */
190
- endpoint?: string;
191
- /** Filter posts by tag */
192
- tag?: string;
193
- /** Maximum number of posts to fetch (default: 500) */
194
- limit?: number;
195
- }
196
-
197
- /**
198
- * Utility function to fetch posts directly (for server-side use cases)
199
- */
200
- export async function fetchPosts(options: FetchPostsOptions): Promise<SeriphPost[]> {
201
- const { endpoint = DEFAULT_ENDPOINT, tag, limit = 500 } = options;
202
- const siteKey = getSiteKey(options);
203
- const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
204
-
205
- const url = new URL(`${baseUrl}/posts`);
206
- url.searchParams.set("limit", String(limit));
207
- if (tag) {
208
- url.searchParams.set("tag", tag);
209
- }
210
-
211
- const response = await fetch(url.toString(), {
212
- headers: {
213
- "X-Seriph-Key": siteKey,
214
- },
215
- });
216
-
217
- if (!response.ok) {
218
- throw new Error(
219
- `Failed to fetch posts: ${response.status} ${response.statusText}`,
220
- );
221
- }
222
-
223
- const data: ApiResponse = await response.json();
224
- return data.posts;
225
- }
226
-
227
- export interface FetchPostOptions {
228
- /** Your site key (required) */
229
- siteKey?: string;
230
- /** @deprecated Use siteKey instead */
231
- apiKey?: string;
232
- /** Base URL of your Seriph instance (default: 'https://seriph.xyz') */
233
- endpoint?: string;
234
- /** The post slug to fetch */
235
- slug: string;
236
- }
237
-
238
- /**
239
- * Utility function to fetch a single post by slug
240
- */
241
- export async function fetchPost(options: FetchPostOptions): Promise<SeriphPost | null> {
242
- const { endpoint = DEFAULT_ENDPOINT, slug } = options;
243
- const siteKey = getSiteKey(options);
244
- const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
245
-
246
- const response = await fetch(`${baseUrl}/posts/${encodeURIComponent(slug)}`, {
247
- headers: {
248
- "X-Seriph-Key": siteKey,
249
- },
250
- });
251
-
252
- if (response.status === 404) {
253
- return null;
254
- }
255
-
256
- if (!response.ok) {
257
- throw new Error(
258
- `Failed to fetch post: ${response.status} ${response.statusText}`,
259
- );
260
- }
261
-
262
- const data = await response.json();
263
- return data.public_post || data;
264
- }