@seriphxyz/core 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +220 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +446 -0
- package/package.json +40 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @seriphxyz/core
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic API client, types, and controllers for Seriph widgets.
|
|
5
|
+
* Use this package directly or through framework-specific wrappers like @seriphxyz/astro.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_ENDPOINT = "https://seriph.xyz";
|
|
8
|
+
export declare const API_PATH = "/api/v1";
|
|
9
|
+
export interface SeriphConfig {
|
|
10
|
+
/** Your site key (required) */
|
|
11
|
+
siteKey: string;
|
|
12
|
+
/** Base URL of your Seriph instance (default: 'https://seriph.xyz') */
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Comment {
|
|
16
|
+
id: string;
|
|
17
|
+
pageId: string;
|
|
18
|
+
parentId?: string;
|
|
19
|
+
authorName: string;
|
|
20
|
+
content: string;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
replies: Comment[];
|
|
23
|
+
}
|
|
24
|
+
export interface ReactionCounts {
|
|
25
|
+
pageId: string;
|
|
26
|
+
counts: Record<string, number>;
|
|
27
|
+
}
|
|
28
|
+
export interface FormSubmitResponse {
|
|
29
|
+
success: boolean;
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SubscribeResponse {
|
|
33
|
+
success: boolean;
|
|
34
|
+
message: string;
|
|
35
|
+
}
|
|
36
|
+
export interface SeriphPost {
|
|
37
|
+
id: string;
|
|
38
|
+
title: string;
|
|
39
|
+
slug: string;
|
|
40
|
+
/** Post content as Markdown */
|
|
41
|
+
content: string;
|
|
42
|
+
/** Raw TipTap/ProseMirror JSON content (for custom rendering) */
|
|
43
|
+
contentJson?: string;
|
|
44
|
+
excerpt?: string;
|
|
45
|
+
coverImage?: string;
|
|
46
|
+
metaTitle?: string;
|
|
47
|
+
metaDescription?: string;
|
|
48
|
+
tags: string[];
|
|
49
|
+
publishedAt: string;
|
|
50
|
+
}
|
|
51
|
+
/** Build full API URL from endpoint and path */
|
|
52
|
+
export declare function buildUrl(endpoint: string | undefined, path: string): string;
|
|
53
|
+
/** Get site key from config */
|
|
54
|
+
export declare function getSiteKey(config: SeriphConfig): string;
|
|
55
|
+
export interface SubmitFormOptions extends SeriphConfig {
|
|
56
|
+
formSlug: string;
|
|
57
|
+
data: Record<string, unknown>;
|
|
58
|
+
/** Form load timestamp for spam detection (auto-set if not provided) */
|
|
59
|
+
formLoadTime?: number;
|
|
60
|
+
}
|
|
61
|
+
export declare function submitForm(options: SubmitFormOptions): Promise<FormSubmitResponse>;
|
|
62
|
+
export interface FetchCommentsOptions extends SeriphConfig {
|
|
63
|
+
pageId: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function fetchComments(options: FetchCommentsOptions): Promise<Comment[]>;
|
|
66
|
+
export interface PostCommentOptions extends SeriphConfig {
|
|
67
|
+
pageId: string;
|
|
68
|
+
authorName: string;
|
|
69
|
+
authorEmail?: string;
|
|
70
|
+
content: string;
|
|
71
|
+
parentId?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare function postComment(options: PostCommentOptions): Promise<Comment>;
|
|
74
|
+
export interface FetchReactionsOptions extends SeriphConfig {
|
|
75
|
+
pageId: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function fetchReactions(options: FetchReactionsOptions): Promise<ReactionCounts>;
|
|
78
|
+
export interface AddReactionOptions extends SeriphConfig {
|
|
79
|
+
pageId: string;
|
|
80
|
+
reactionType?: string;
|
|
81
|
+
}
|
|
82
|
+
export declare function addReaction(options: AddReactionOptions): Promise<{
|
|
83
|
+
reactionType: string;
|
|
84
|
+
count: number;
|
|
85
|
+
}>;
|
|
86
|
+
export interface RemoveReactionOptions extends SeriphConfig {
|
|
87
|
+
pageId: string;
|
|
88
|
+
reactionType?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare function removeReaction(options: RemoveReactionOptions): Promise<{
|
|
91
|
+
reactionType: string;
|
|
92
|
+
count: number;
|
|
93
|
+
}>;
|
|
94
|
+
export interface SubscribeOptions extends SeriphConfig {
|
|
95
|
+
email: string;
|
|
96
|
+
}
|
|
97
|
+
export declare function subscribe(options: SubscribeOptions): Promise<SubscribeResponse>;
|
|
98
|
+
export interface FetchPostsOptions extends SeriphConfig {
|
|
99
|
+
/** Filter posts by tag */
|
|
100
|
+
tag?: string;
|
|
101
|
+
/** Maximum number of posts to fetch (default: 500) */
|
|
102
|
+
limit?: number;
|
|
103
|
+
}
|
|
104
|
+
export declare function fetchPosts(options: FetchPostsOptions): Promise<SeriphPost[]>;
|
|
105
|
+
export interface FetchPostOptions extends SeriphConfig {
|
|
106
|
+
/** The post slug to fetch */
|
|
107
|
+
slug: string;
|
|
108
|
+
}
|
|
109
|
+
export declare function fetchPost(options: FetchPostOptions): Promise<SeriphPost | null>;
|
|
110
|
+
export type ControllerStatus = "idle" | "loading" | "success" | "error";
|
|
111
|
+
/** State for subscribe controllers */
|
|
112
|
+
export interface SubscribeState {
|
|
113
|
+
status: ControllerStatus;
|
|
114
|
+
message: string | null;
|
|
115
|
+
error: Error | null;
|
|
116
|
+
}
|
|
117
|
+
/** State for form controllers */
|
|
118
|
+
export interface FormState {
|
|
119
|
+
status: ControllerStatus;
|
|
120
|
+
message: string | null;
|
|
121
|
+
error: Error | null;
|
|
122
|
+
}
|
|
123
|
+
/** State for reactions controllers */
|
|
124
|
+
export interface ReactionsState {
|
|
125
|
+
counts: Record<string, number>;
|
|
126
|
+
userReactions: string[];
|
|
127
|
+
status: ControllerStatus;
|
|
128
|
+
error: Error | null;
|
|
129
|
+
}
|
|
130
|
+
/** State for comments controllers */
|
|
131
|
+
export interface CommentsState {
|
|
132
|
+
comments: Comment[];
|
|
133
|
+
status: ControllerStatus;
|
|
134
|
+
error: Error | null;
|
|
135
|
+
}
|
|
136
|
+
export interface ControllerListener<T> {
|
|
137
|
+
(state: T): void;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Headless controller for subscribe forms.
|
|
141
|
+
* Manages state without any DOM/framework dependencies.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* const controller = new SubscribeController({ siteKey: 'xxx' });
|
|
145
|
+
* controller.subscribe((state) => {
|
|
146
|
+
* console.log(state.status, state.message, state.error);
|
|
147
|
+
* });
|
|
148
|
+
* await controller.submit('user@example.com');
|
|
149
|
+
*/
|
|
150
|
+
export declare class SubscribeController {
|
|
151
|
+
private config;
|
|
152
|
+
private listeners;
|
|
153
|
+
private _state;
|
|
154
|
+
constructor(config: SeriphConfig);
|
|
155
|
+
/** Get current state */
|
|
156
|
+
getState(): SubscribeState;
|
|
157
|
+
/** Subscribe to state changes */
|
|
158
|
+
subscribe(listener: ControllerListener<SubscribeState>): () => void;
|
|
159
|
+
private notify;
|
|
160
|
+
/** Submit email for subscription */
|
|
161
|
+
submit(email: string): Promise<SubscribeResponse>;
|
|
162
|
+
/** Reset to idle state */
|
|
163
|
+
reset(): void;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Headless controller for forms.
|
|
167
|
+
* Manages state without any DOM/framework dependencies.
|
|
168
|
+
*/
|
|
169
|
+
export declare class FormController {
|
|
170
|
+
private config;
|
|
171
|
+
private formSlug;
|
|
172
|
+
private listeners;
|
|
173
|
+
private loadTime;
|
|
174
|
+
private _state;
|
|
175
|
+
constructor(config: SeriphConfig, formSlug: string);
|
|
176
|
+
getState(): FormState;
|
|
177
|
+
subscribe(listener: ControllerListener<FormState>): () => void;
|
|
178
|
+
private notify;
|
|
179
|
+
submit(data: Record<string, unknown>): Promise<FormSubmitResponse>;
|
|
180
|
+
reset(): void;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Headless controller for reactions.
|
|
184
|
+
* Manages state and counts without any DOM/framework dependencies.
|
|
185
|
+
*/
|
|
186
|
+
export declare class ReactionsController {
|
|
187
|
+
private config;
|
|
188
|
+
private pageId;
|
|
189
|
+
private listeners;
|
|
190
|
+
private _state;
|
|
191
|
+
constructor(config: SeriphConfig, pageId: string);
|
|
192
|
+
getState(): ReactionsState;
|
|
193
|
+
subscribe(listener: ControllerListener<ReactionsState>): () => void;
|
|
194
|
+
private notify;
|
|
195
|
+
/** Fetch reactions from the server */
|
|
196
|
+
fetch(): Promise<ReactionsState>;
|
|
197
|
+
add(reactionType?: string): Promise<void>;
|
|
198
|
+
remove(reactionType?: string): Promise<void>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Headless controller for comments.
|
|
202
|
+
* Manages state and comment list without any DOM/framework dependencies.
|
|
203
|
+
*/
|
|
204
|
+
export declare class CommentsController {
|
|
205
|
+
private config;
|
|
206
|
+
private pageId;
|
|
207
|
+
private listeners;
|
|
208
|
+
private _state;
|
|
209
|
+
constructor(config: SeriphConfig, pageId: string);
|
|
210
|
+
getState(): CommentsState;
|
|
211
|
+
subscribe(listener: ControllerListener<CommentsState>): () => void;
|
|
212
|
+
private notify;
|
|
213
|
+
/** Fetch comments from the server */
|
|
214
|
+
fetch(): Promise<Comment[]>;
|
|
215
|
+
post(authorName: string, content: string, options?: {
|
|
216
|
+
authorEmail?: string;
|
|
217
|
+
parentId?: string;
|
|
218
|
+
}): Promise<Comment>;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @seriphxyz/core
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic API client, types, and controllers for Seriph widgets.
|
|
5
|
+
* Use this package directly or through framework-specific wrappers like @seriphxyz/astro.
|
|
6
|
+
*/
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// Constants
|
|
9
|
+
// =============================================================================
|
|
10
|
+
export const DEFAULT_ENDPOINT = "https://seriph.xyz";
|
|
11
|
+
export const API_PATH = "/api/v1";
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Helpers
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/** Build full API URL from endpoint and path */
|
|
16
|
+
export function buildUrl(endpoint, path) {
|
|
17
|
+
const base = (endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
18
|
+
return `${base}${API_PATH}${path}`;
|
|
19
|
+
}
|
|
20
|
+
/** Get site key from config */
|
|
21
|
+
export function getSiteKey(config) {
|
|
22
|
+
if (!config.siteKey) {
|
|
23
|
+
throw new Error("siteKey is required");
|
|
24
|
+
}
|
|
25
|
+
return config.siteKey;
|
|
26
|
+
}
|
|
27
|
+
export async function submitForm(options) {
|
|
28
|
+
const { endpoint, formSlug, data, formLoadTime } = options;
|
|
29
|
+
const siteKey = getSiteKey(options);
|
|
30
|
+
const url = buildUrl(endpoint, `/forms/${formSlug}/submit`);
|
|
31
|
+
const payload = {
|
|
32
|
+
...data,
|
|
33
|
+
_seriph_ts: formLoadTime || Math.floor(Date.now() / 1000),
|
|
34
|
+
};
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"X-Seriph-Key": siteKey,
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify(payload),
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`Form submission failed: ${response.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
return response.json();
|
|
47
|
+
}
|
|
48
|
+
export async function fetchComments(options) {
|
|
49
|
+
const { endpoint, pageId } = options;
|
|
50
|
+
const siteKey = getSiteKey(options);
|
|
51
|
+
const url = buildUrl(endpoint, `/comments/${encodeURIComponent(pageId)}`);
|
|
52
|
+
const response = await fetch(url, {
|
|
53
|
+
headers: {
|
|
54
|
+
"X-Seriph-Key": siteKey,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`Failed to fetch comments: ${response.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
return data.data || [];
|
|
62
|
+
}
|
|
63
|
+
export async function postComment(options) {
|
|
64
|
+
const { endpoint, pageId, authorName, authorEmail, content, parentId } = options;
|
|
65
|
+
const siteKey = getSiteKey(options);
|
|
66
|
+
const url = buildUrl(endpoint, `/comments/${encodeURIComponent(pageId)}`);
|
|
67
|
+
const response = await fetch(url, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
"X-Seriph-Key": siteKey,
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
authorName,
|
|
75
|
+
authorEmail,
|
|
76
|
+
content,
|
|
77
|
+
parentId,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`Failed to post comment: ${response.statusText}`);
|
|
82
|
+
}
|
|
83
|
+
const data = await response.json();
|
|
84
|
+
return data.data;
|
|
85
|
+
}
|
|
86
|
+
export async function fetchReactions(options) {
|
|
87
|
+
const { endpoint, pageId } = options;
|
|
88
|
+
const siteKey = getSiteKey(options);
|
|
89
|
+
const url = buildUrl(endpoint, `/reactions/${encodeURIComponent(pageId)}`);
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
headers: {
|
|
92
|
+
"X-Seriph-Key": siteKey,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`Failed to fetch reactions: ${response.statusText}`);
|
|
97
|
+
}
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
return data.data;
|
|
100
|
+
}
|
|
101
|
+
export async function addReaction(options) {
|
|
102
|
+
const { endpoint, pageId, reactionType = "like" } = options;
|
|
103
|
+
const siteKey = getSiteKey(options);
|
|
104
|
+
const url = buildUrl(endpoint, `/reactions/${encodeURIComponent(pageId)}`);
|
|
105
|
+
const response = await fetch(url, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "application/json",
|
|
109
|
+
"X-Seriph-Key": siteKey,
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify({ reactionType }),
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error(`Failed to add reaction: ${response.statusText}`);
|
|
115
|
+
}
|
|
116
|
+
const data = await response.json();
|
|
117
|
+
return data.data;
|
|
118
|
+
}
|
|
119
|
+
export async function removeReaction(options) {
|
|
120
|
+
const { endpoint, pageId, reactionType = "like" } = options;
|
|
121
|
+
const siteKey = getSiteKey(options);
|
|
122
|
+
const url = buildUrl(endpoint, `/reactions/${encodeURIComponent(pageId)}`);
|
|
123
|
+
const response = await fetch(url, {
|
|
124
|
+
method: "DELETE",
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
"X-Seriph-Key": siteKey,
|
|
128
|
+
},
|
|
129
|
+
body: JSON.stringify({ reactionType }),
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
throw new Error(`Failed to remove reaction: ${response.statusText}`);
|
|
133
|
+
}
|
|
134
|
+
const data = await response.json();
|
|
135
|
+
return data.data;
|
|
136
|
+
}
|
|
137
|
+
export async function subscribe(options) {
|
|
138
|
+
const { endpoint, email } = options;
|
|
139
|
+
const siteKey = getSiteKey(options);
|
|
140
|
+
const url = buildUrl(endpoint, "/subscribe");
|
|
141
|
+
const response = await fetch(url, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
"X-Seriph-Key": siteKey,
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({ email }),
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
throw new Error(`Subscription failed: ${response.statusText}`);
|
|
151
|
+
}
|
|
152
|
+
return response.json();
|
|
153
|
+
}
|
|
154
|
+
export async function fetchPosts(options) {
|
|
155
|
+
const { endpoint, tag, limit = 500 } = options;
|
|
156
|
+
const siteKey = getSiteKey(options);
|
|
157
|
+
const baseUrl = (endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, "") + API_PATH;
|
|
158
|
+
const url = new URL(`${baseUrl}/posts`);
|
|
159
|
+
url.searchParams.set("limit", String(limit));
|
|
160
|
+
if (tag) {
|
|
161
|
+
url.searchParams.set("tag", tag);
|
|
162
|
+
}
|
|
163
|
+
const response = await fetch(url.toString(), {
|
|
164
|
+
headers: {
|
|
165
|
+
"X-Seriph-Key": siteKey,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
if (!response.ok) {
|
|
169
|
+
throw new Error(`Failed to fetch posts: ${response.status} ${response.statusText}`);
|
|
170
|
+
}
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
return data.posts;
|
|
173
|
+
}
|
|
174
|
+
export async function fetchPost(options) {
|
|
175
|
+
const { endpoint, slug } = options;
|
|
176
|
+
const siteKey = getSiteKey(options);
|
|
177
|
+
const baseUrl = (endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, "") + API_PATH;
|
|
178
|
+
const response = await fetch(`${baseUrl}/posts/${encodeURIComponent(slug)}`, {
|
|
179
|
+
headers: {
|
|
180
|
+
"X-Seriph-Key": siteKey,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
if (response.status === 404) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
throw new Error(`Failed to fetch post: ${response.status} ${response.statusText}`);
|
|
188
|
+
}
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
return data.public_post || data;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Headless controller for subscribe forms.
|
|
194
|
+
* Manages state without any DOM/framework dependencies.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* const controller = new SubscribeController({ siteKey: 'xxx' });
|
|
198
|
+
* controller.subscribe((state) => {
|
|
199
|
+
* console.log(state.status, state.message, state.error);
|
|
200
|
+
* });
|
|
201
|
+
* await controller.submit('user@example.com');
|
|
202
|
+
*/
|
|
203
|
+
export class SubscribeController {
|
|
204
|
+
config;
|
|
205
|
+
listeners = new Set();
|
|
206
|
+
_state = { status: "idle", message: null, error: null };
|
|
207
|
+
constructor(config) {
|
|
208
|
+
this.config = config;
|
|
209
|
+
}
|
|
210
|
+
/** Get current state */
|
|
211
|
+
getState() {
|
|
212
|
+
return { ...this._state };
|
|
213
|
+
}
|
|
214
|
+
/** Subscribe to state changes */
|
|
215
|
+
subscribe(listener) {
|
|
216
|
+
this.listeners.add(listener);
|
|
217
|
+
// Immediately call with current state
|
|
218
|
+
listener(this.getState());
|
|
219
|
+
return () => this.listeners.delete(listener);
|
|
220
|
+
}
|
|
221
|
+
notify() {
|
|
222
|
+
const state = this.getState();
|
|
223
|
+
for (const listener of this.listeners) {
|
|
224
|
+
listener(state);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Submit email for subscription */
|
|
228
|
+
async submit(email) {
|
|
229
|
+
this._state = { status: "loading", message: null, error: null };
|
|
230
|
+
this.notify();
|
|
231
|
+
try {
|
|
232
|
+
const result = await subscribe({ ...this.config, email });
|
|
233
|
+
this._state = { status: "success", message: result.message, error: null };
|
|
234
|
+
this.notify();
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
239
|
+
this._state = { status: "error", message: error.message, error };
|
|
240
|
+
this.notify();
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/** Reset to idle state */
|
|
245
|
+
reset() {
|
|
246
|
+
this._state = { status: "idle", message: null, error: null };
|
|
247
|
+
this.notify();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Headless controller for forms.
|
|
252
|
+
* Manages state without any DOM/framework dependencies.
|
|
253
|
+
*/
|
|
254
|
+
export class FormController {
|
|
255
|
+
config;
|
|
256
|
+
formSlug;
|
|
257
|
+
listeners = new Set();
|
|
258
|
+
loadTime;
|
|
259
|
+
_state = { status: "idle", message: null, error: null };
|
|
260
|
+
constructor(config, formSlug) {
|
|
261
|
+
this.config = config;
|
|
262
|
+
this.formSlug = formSlug;
|
|
263
|
+
this.loadTime = Math.floor(Date.now() / 1000);
|
|
264
|
+
}
|
|
265
|
+
getState() {
|
|
266
|
+
return { ...this._state };
|
|
267
|
+
}
|
|
268
|
+
subscribe(listener) {
|
|
269
|
+
this.listeners.add(listener);
|
|
270
|
+
listener(this.getState());
|
|
271
|
+
return () => this.listeners.delete(listener);
|
|
272
|
+
}
|
|
273
|
+
notify() {
|
|
274
|
+
const state = this.getState();
|
|
275
|
+
for (const listener of this.listeners) {
|
|
276
|
+
listener(state);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async submit(data) {
|
|
280
|
+
this._state = { status: "loading", message: null, error: null };
|
|
281
|
+
this.notify();
|
|
282
|
+
try {
|
|
283
|
+
const result = await submitForm({
|
|
284
|
+
...this.config,
|
|
285
|
+
formSlug: this.formSlug,
|
|
286
|
+
data,
|
|
287
|
+
formLoadTime: this.loadTime,
|
|
288
|
+
});
|
|
289
|
+
this._state = { status: "success", message: result.message, error: null };
|
|
290
|
+
this.notify();
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
295
|
+
this._state = { status: "error", message: error.message, error };
|
|
296
|
+
this.notify();
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
reset() {
|
|
301
|
+
this._state = { status: "idle", message: null, error: null };
|
|
302
|
+
this.loadTime = Math.floor(Date.now() / 1000);
|
|
303
|
+
this.notify();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Headless controller for reactions.
|
|
308
|
+
* Manages state and counts without any DOM/framework dependencies.
|
|
309
|
+
*/
|
|
310
|
+
export class ReactionsController {
|
|
311
|
+
config;
|
|
312
|
+
pageId;
|
|
313
|
+
listeners = new Set();
|
|
314
|
+
_state = { counts: {}, userReactions: [], status: "idle", error: null };
|
|
315
|
+
constructor(config, pageId) {
|
|
316
|
+
this.config = config;
|
|
317
|
+
this.pageId = pageId;
|
|
318
|
+
}
|
|
319
|
+
getState() {
|
|
320
|
+
return { ...this._state, counts: { ...this._state.counts }, userReactions: [...this._state.userReactions] };
|
|
321
|
+
}
|
|
322
|
+
subscribe(listener) {
|
|
323
|
+
this.listeners.add(listener);
|
|
324
|
+
listener(this.getState());
|
|
325
|
+
return () => this.listeners.delete(listener);
|
|
326
|
+
}
|
|
327
|
+
notify() {
|
|
328
|
+
const state = this.getState();
|
|
329
|
+
for (const listener of this.listeners) {
|
|
330
|
+
listener(state);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/** Fetch reactions from the server */
|
|
334
|
+
async fetch() {
|
|
335
|
+
this._state = { ...this._state, status: "loading", error: null };
|
|
336
|
+
this.notify();
|
|
337
|
+
try {
|
|
338
|
+
const result = await fetchReactions({ ...this.config, pageId: this.pageId });
|
|
339
|
+
this._state = { ...this._state, counts: result.counts, status: "success", error: null };
|
|
340
|
+
this.notify();
|
|
341
|
+
return this.getState();
|
|
342
|
+
}
|
|
343
|
+
catch (e) {
|
|
344
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
345
|
+
this._state = { ...this._state, status: "error", error };
|
|
346
|
+
this.notify();
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async add(reactionType = "like") {
|
|
351
|
+
try {
|
|
352
|
+
const result = await addReaction({ ...this.config, pageId: this.pageId, reactionType });
|
|
353
|
+
this._state.counts[reactionType] = result.count;
|
|
354
|
+
if (!this._state.userReactions.includes(reactionType)) {
|
|
355
|
+
this._state.userReactions = [...this._state.userReactions, reactionType];
|
|
356
|
+
}
|
|
357
|
+
this.notify();
|
|
358
|
+
}
|
|
359
|
+
catch (e) {
|
|
360
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
361
|
+
this._state = { ...this._state, error };
|
|
362
|
+
this.notify();
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async remove(reactionType = "like") {
|
|
367
|
+
try {
|
|
368
|
+
const result = await removeReaction({ ...this.config, pageId: this.pageId, reactionType });
|
|
369
|
+
this._state.counts[reactionType] = result.count;
|
|
370
|
+
this._state.userReactions = this._state.userReactions.filter(r => r !== reactionType);
|
|
371
|
+
this.notify();
|
|
372
|
+
}
|
|
373
|
+
catch (e) {
|
|
374
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
375
|
+
this._state = { ...this._state, error };
|
|
376
|
+
this.notify();
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Headless controller for comments.
|
|
383
|
+
* Manages state and comment list without any DOM/framework dependencies.
|
|
384
|
+
*/
|
|
385
|
+
export class CommentsController {
|
|
386
|
+
config;
|
|
387
|
+
pageId;
|
|
388
|
+
listeners = new Set();
|
|
389
|
+
_state = { comments: [], status: "idle", error: null };
|
|
390
|
+
constructor(config, pageId) {
|
|
391
|
+
this.config = config;
|
|
392
|
+
this.pageId = pageId;
|
|
393
|
+
}
|
|
394
|
+
getState() {
|
|
395
|
+
return { ...this._state, comments: [...this._state.comments] };
|
|
396
|
+
}
|
|
397
|
+
subscribe(listener) {
|
|
398
|
+
this.listeners.add(listener);
|
|
399
|
+
listener(this.getState());
|
|
400
|
+
return () => this.listeners.delete(listener);
|
|
401
|
+
}
|
|
402
|
+
notify() {
|
|
403
|
+
const state = this.getState();
|
|
404
|
+
for (const listener of this.listeners) {
|
|
405
|
+
listener(state);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** Fetch comments from the server */
|
|
409
|
+
async fetch() {
|
|
410
|
+
this._state = { ...this._state, status: "loading", error: null };
|
|
411
|
+
this.notify();
|
|
412
|
+
try {
|
|
413
|
+
const comments = await fetchComments({ ...this.config, pageId: this.pageId });
|
|
414
|
+
this._state = { comments, status: "success", error: null };
|
|
415
|
+
this.notify();
|
|
416
|
+
return comments;
|
|
417
|
+
}
|
|
418
|
+
catch (e) {
|
|
419
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
420
|
+
this._state = { ...this._state, status: "error", error };
|
|
421
|
+
this.notify();
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async post(authorName, content, options) {
|
|
426
|
+
try {
|
|
427
|
+
const comment = await postComment({
|
|
428
|
+
...this.config,
|
|
429
|
+
pageId: this.pageId,
|
|
430
|
+
authorName,
|
|
431
|
+
content,
|
|
432
|
+
authorEmail: options?.authorEmail,
|
|
433
|
+
parentId: options?.parentId,
|
|
434
|
+
});
|
|
435
|
+
// Reload to get updated tree structure
|
|
436
|
+
await this.fetch();
|
|
437
|
+
return comment;
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
441
|
+
this._state = { ...this._state, error };
|
|
442
|
+
this.notify();
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seriphxyz/core",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Framework-agnostic API client and types for Seriph widgets",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/seriphxyz/core.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://seriph.xyz",
|
|
10
|
+
"author": "Tim Shedor",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"seriph",
|
|
25
|
+
"api",
|
|
26
|
+
"forms",
|
|
27
|
+
"comments",
|
|
28
|
+
"reactions",
|
|
29
|
+
"subscribe",
|
|
30
|
+
"widgets"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.7.3"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"dev": "tsc --watch"
|
|
39
|
+
}
|
|
40
|
+
}
|