@foru-ms/sdk 0.1.0
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/Client.d.ts +21 -0
- package/dist/Client.js +52 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +23 -0
- package/dist/resources/Auth.d.ts +12 -0
- package/dist/resources/Auth.js +30 -0
- package/dist/resources/Posts.d.ts +21 -0
- package/dist/resources/Posts.js +45 -0
- package/dist/resources/Tags.d.ts +10 -0
- package/dist/resources/Tags.js +22 -0
- package/dist/resources/Threads.d.ts +22 -0
- package/dist/resources/Threads.js +45 -0
- package/dist/resources/Users.d.ts +12 -0
- package/dist/resources/Users.js +27 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.js +2 -0
- package/package.json +20 -0
- package/src/Client.ts +66 -0
- package/src/index.ts +7 -0
- package/src/resources/Auth.ts +36 -0
- package/src/resources/Posts.ts +58 -0
- package/src/resources/Tags.ts +28 -0
- package/src/resources/Threads.ts +59 -0
- package/src/resources/Users.ts +35 -0
- package/src/types.ts +156 -0
- package/tsconfig.json +21 -0
package/dist/Client.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AuthResource } from './resources/Auth';
|
|
2
|
+
import { ThreadsResource } from './resources/Threads';
|
|
3
|
+
import { PostsResource } from './resources/Posts';
|
|
4
|
+
import { UsersResource } from './resources/Users';
|
|
5
|
+
import { TagsResource } from './resources/Tags';
|
|
6
|
+
export declare class ForumClient {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
token: string | null;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
auth: AuthResource;
|
|
11
|
+
threads: ThreadsResource;
|
|
12
|
+
posts: PostsResource;
|
|
13
|
+
users: UsersResource;
|
|
14
|
+
tags: TagsResource;
|
|
15
|
+
constructor(options: {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
});
|
|
19
|
+
request<T>(path: string, options?: RequestInit): Promise<T>;
|
|
20
|
+
setToken(token: string): void;
|
|
21
|
+
}
|
package/dist/Client.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForumClient = void 0;
|
|
4
|
+
const Auth_1 = require("./resources/Auth");
|
|
5
|
+
const Threads_1 = require("./resources/Threads");
|
|
6
|
+
const Posts_1 = require("./resources/Posts");
|
|
7
|
+
const Users_1 = require("./resources/Users");
|
|
8
|
+
const Tags_1 = require("./resources/Tags");
|
|
9
|
+
// Polyfill fetch if needed (e.g. older Node versions)
|
|
10
|
+
const fetch = globalThis.fetch || require('cross-fetch');
|
|
11
|
+
class ForumClient {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.token = null;
|
|
14
|
+
this.apiKey = options.apiKey;
|
|
15
|
+
this.baseUrl = options.baseUrl || 'https://api.foru.ms/v1';
|
|
16
|
+
this.auth = new Auth_1.AuthResource(this);
|
|
17
|
+
this.threads = new Threads_1.ThreadsResource(this);
|
|
18
|
+
this.posts = new Posts_1.PostsResource(this);
|
|
19
|
+
this.users = new Users_1.UsersResource(this);
|
|
20
|
+
this.tags = new Tags_1.TagsResource(this);
|
|
21
|
+
}
|
|
22
|
+
async request(path, options = {}) {
|
|
23
|
+
const headers = {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'x-api-key': this.apiKey,
|
|
26
|
+
...options.headers,
|
|
27
|
+
};
|
|
28
|
+
if (this.token) {
|
|
29
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
30
|
+
}
|
|
31
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
32
|
+
...options,
|
|
33
|
+
headers,
|
|
34
|
+
});
|
|
35
|
+
const contentType = response.headers.get('content-type');
|
|
36
|
+
let data;
|
|
37
|
+
if (contentType && contentType.includes('application/json')) {
|
|
38
|
+
data = await response.json();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
data = await response.text();
|
|
42
|
+
}
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error((data && data.message) || data.error || 'API Error');
|
|
45
|
+
}
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
setToken(token) {
|
|
49
|
+
this.token = token;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.ForumClient = ForumClient;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Client"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./resources/Auth"), exports);
|
|
20
|
+
__exportStar(require("./resources/Threads"), exports);
|
|
21
|
+
__exportStar(require("./resources/Posts"), exports);
|
|
22
|
+
__exportStar(require("./resources/Users"), exports);
|
|
23
|
+
__exportStar(require("./resources/Tags"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { LoginResponse } from '../types';
|
|
3
|
+
export declare class AuthResource {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: ForumClient);
|
|
6
|
+
login(payload: {
|
|
7
|
+
login: string;
|
|
8
|
+
password: string;
|
|
9
|
+
}): Promise<LoginResponse>;
|
|
10
|
+
register(payload: import('../types').RegisterPayload): Promise<import('../types').User>;
|
|
11
|
+
me(): Promise<import('../types').User>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthResource = void 0;
|
|
4
|
+
class AuthResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
async login(payload) {
|
|
9
|
+
const response = await this.client.request('/auth/login', {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
body: JSON.stringify(payload),
|
|
12
|
+
});
|
|
13
|
+
if (response.token) {
|
|
14
|
+
this.client.setToken(response.token);
|
|
15
|
+
}
|
|
16
|
+
return response;
|
|
17
|
+
}
|
|
18
|
+
async register(payload) {
|
|
19
|
+
return this.client.request('/auth/register', {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
body: JSON.stringify(payload),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async me() {
|
|
25
|
+
return this.client.request('/auth/me', {
|
|
26
|
+
method: 'GET',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.AuthResource = AuthResource;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { PostListResponse, InteractionType } from '../types';
|
|
3
|
+
export declare class PostsResource {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: ForumClient);
|
|
6
|
+
list(params?: {
|
|
7
|
+
query?: string;
|
|
8
|
+
filter?: 'newest' | 'oldest';
|
|
9
|
+
type?: InteractionType;
|
|
10
|
+
cursor?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
}): Promise<PostListResponse>;
|
|
13
|
+
create(payload: import('../types').CreatePostPayload): Promise<import('../types').Post>;
|
|
14
|
+
retrieve(postId: string): Promise<import('../types').Post>;
|
|
15
|
+
update(postId: string, payload: import('../types').UpdatePostPayload): Promise<import('../types').Post>;
|
|
16
|
+
delete(postId: string, payload?: {
|
|
17
|
+
userId: string;
|
|
18
|
+
}): Promise<import('../types').Post & {
|
|
19
|
+
deleted: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostsResource = void 0;
|
|
4
|
+
class PostsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
async list(params) {
|
|
9
|
+
const searchParams = new URLSearchParams();
|
|
10
|
+
if (params) {
|
|
11
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
searchParams.append(key, value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return this.client.request(`/posts?${searchParams.toString()}`, {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async create(payload) {
|
|
22
|
+
return this.client.request('/post', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify(payload),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async retrieve(postId) {
|
|
28
|
+
return this.client.request(`/post/${postId}`, {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async update(postId, payload) {
|
|
33
|
+
return this.client.request(`/post/${postId}`, {
|
|
34
|
+
method: 'PUT',
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async delete(postId, payload) {
|
|
39
|
+
return this.client.request(`/post/${postId}`, {
|
|
40
|
+
method: 'DELETE',
|
|
41
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.PostsResource = PostsResource;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { TagListResponse } from '../types';
|
|
3
|
+
export declare class TagsResource {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: ForumClient);
|
|
6
|
+
list(params?: {
|
|
7
|
+
query?: string;
|
|
8
|
+
cursor?: string;
|
|
9
|
+
}): Promise<TagListResponse>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TagsResource = void 0;
|
|
4
|
+
class TagsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
async list(params) {
|
|
9
|
+
const searchParams = new URLSearchParams();
|
|
10
|
+
if (params) {
|
|
11
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
searchParams.append(key, value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return this.client.request(`/tags?${searchParams.toString()}`, {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.TagsResource = TagsResource;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { ThreadListResponse, ThreadFilter, InteractionType } from '../types';
|
|
3
|
+
export declare class ThreadsResource {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: ForumClient);
|
|
6
|
+
list(params?: {
|
|
7
|
+
query?: string;
|
|
8
|
+
tagId?: string;
|
|
9
|
+
filter?: ThreadFilter;
|
|
10
|
+
type?: InteractionType;
|
|
11
|
+
cursor?: string;
|
|
12
|
+
userId?: string;
|
|
13
|
+
}): Promise<ThreadListResponse>;
|
|
14
|
+
create(payload: import('../types').CreateThreadPayload): Promise<import('../types').Thread>;
|
|
15
|
+
retrieve(threadId: string): Promise<import('../types').Thread>;
|
|
16
|
+
update(threadId: string, payload: import('../types').UpdateThreadPayload): Promise<import('../types').Thread>;
|
|
17
|
+
delete(threadId: string, payload?: {
|
|
18
|
+
userId: string;
|
|
19
|
+
}): Promise<import('../types').Thread & {
|
|
20
|
+
deleted: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ThreadsResource = void 0;
|
|
4
|
+
class ThreadsResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
async list(params) {
|
|
9
|
+
const searchParams = new URLSearchParams();
|
|
10
|
+
if (params) {
|
|
11
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
searchParams.append(key, value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return this.client.request(`/threads?${searchParams.toString()}`, {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async create(payload) {
|
|
22
|
+
return this.client.request('/thread', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify(payload),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async retrieve(threadId) {
|
|
28
|
+
return this.client.request(`/thread/${threadId}`, {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async update(threadId, payload) {
|
|
33
|
+
return this.client.request(`/thread/${threadId}`, {
|
|
34
|
+
method: 'PUT',
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async delete(threadId, payload) {
|
|
39
|
+
return this.client.request(`/thread/${threadId}`, {
|
|
40
|
+
method: 'DELETE',
|
|
41
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ThreadsResource = ThreadsResource;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { UserListResponse, User } from '../types';
|
|
3
|
+
export declare class UsersResource {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: ForumClient);
|
|
6
|
+
list(params?: {
|
|
7
|
+
query?: string;
|
|
8
|
+
filter?: 'newest' | 'oldest';
|
|
9
|
+
cursor?: string;
|
|
10
|
+
}): Promise<UserListResponse>;
|
|
11
|
+
retrieve(userId: string): Promise<User>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UsersResource = void 0;
|
|
4
|
+
class UsersResource {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
async list(params) {
|
|
9
|
+
const searchParams = new URLSearchParams();
|
|
10
|
+
if (params) {
|
|
11
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
searchParams.append(key, value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return this.client.request(`/users?${searchParams.toString()}`, {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async retrieve(userId) {
|
|
22
|
+
return this.client.request(`/user/${userId}`, {
|
|
23
|
+
method: 'GET',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.UsersResource = UsersResource;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export interface RegisterPayload {
|
|
2
|
+
username: string;
|
|
3
|
+
email: string;
|
|
4
|
+
password: string;
|
|
5
|
+
displayName?: string;
|
|
6
|
+
emailVerified?: boolean;
|
|
7
|
+
roles?: string[];
|
|
8
|
+
extendedData?: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
export interface User {
|
|
11
|
+
id: string;
|
|
12
|
+
username: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
displayName: string | null;
|
|
15
|
+
roles?: any[];
|
|
16
|
+
extendedData?: Record<string, any>;
|
|
17
|
+
createdAt?: string;
|
|
18
|
+
updatedAt?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface Thread {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
body: string;
|
|
25
|
+
userId: string;
|
|
26
|
+
locked: boolean;
|
|
27
|
+
pinned: boolean;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
updatedAt: string;
|
|
30
|
+
user?: User;
|
|
31
|
+
likes?: any[];
|
|
32
|
+
upvotes?: any[];
|
|
33
|
+
tags?: any[];
|
|
34
|
+
poll?: any;
|
|
35
|
+
extendedData?: Record<string, any>;
|
|
36
|
+
_count?: {
|
|
37
|
+
Post: number;
|
|
38
|
+
likes?: number;
|
|
39
|
+
upvotes?: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface CreateThreadPayload {
|
|
43
|
+
title: string;
|
|
44
|
+
body: string;
|
|
45
|
+
userId?: string;
|
|
46
|
+
slug?: string;
|
|
47
|
+
locked?: boolean;
|
|
48
|
+
pinned?: boolean;
|
|
49
|
+
tags?: string[];
|
|
50
|
+
poll?: {
|
|
51
|
+
title: string;
|
|
52
|
+
options: {
|
|
53
|
+
title: string;
|
|
54
|
+
color?: string;
|
|
55
|
+
extendedData?: Record<string, any>;
|
|
56
|
+
}[];
|
|
57
|
+
};
|
|
58
|
+
extendedData?: Record<string, any>;
|
|
59
|
+
}
|
|
60
|
+
export interface UpdateThreadPayload {
|
|
61
|
+
title?: string;
|
|
62
|
+
slug?: string;
|
|
63
|
+
body?: string;
|
|
64
|
+
userId?: string;
|
|
65
|
+
locked?: boolean;
|
|
66
|
+
pinned?: boolean;
|
|
67
|
+
extendedData?: Record<string, any>;
|
|
68
|
+
}
|
|
69
|
+
export interface Post {
|
|
70
|
+
id: string;
|
|
71
|
+
body: string;
|
|
72
|
+
threadId: string;
|
|
73
|
+
userId: string;
|
|
74
|
+
parentId?: string | null;
|
|
75
|
+
depth?: number;
|
|
76
|
+
createdAt: string;
|
|
77
|
+
updatedAt: string;
|
|
78
|
+
user?: User;
|
|
79
|
+
Thread?: {
|
|
80
|
+
userId: string;
|
|
81
|
+
title: string;
|
|
82
|
+
};
|
|
83
|
+
extendedData?: Record<string, any>;
|
|
84
|
+
}
|
|
85
|
+
export interface CreatePostPayload {
|
|
86
|
+
body: string;
|
|
87
|
+
threadId: string;
|
|
88
|
+
userId?: string;
|
|
89
|
+
parentId?: string;
|
|
90
|
+
extendedData?: Record<string, any>;
|
|
91
|
+
}
|
|
92
|
+
export interface UpdatePostPayload {
|
|
93
|
+
body?: string;
|
|
94
|
+
userId?: string;
|
|
95
|
+
threadId?: string;
|
|
96
|
+
parentId?: string;
|
|
97
|
+
bestAnswer?: boolean;
|
|
98
|
+
extendedData?: Record<string, any>;
|
|
99
|
+
}
|
|
100
|
+
export interface PaginatedResponse<T> {
|
|
101
|
+
data: T[];
|
|
102
|
+
}
|
|
103
|
+
export interface ThreadListResponse {
|
|
104
|
+
threads: Thread[];
|
|
105
|
+
nextThreadCursor?: string;
|
|
106
|
+
count: number;
|
|
107
|
+
}
|
|
108
|
+
export interface PostListResponse {
|
|
109
|
+
posts: Post[];
|
|
110
|
+
nextPostCursor?: string;
|
|
111
|
+
count: number;
|
|
112
|
+
}
|
|
113
|
+
export interface LoginResponse {
|
|
114
|
+
token: string;
|
|
115
|
+
}
|
|
116
|
+
export type ThreadFilter = 'newest' | 'oldest';
|
|
117
|
+
export type InteractionType = 'created' | 'liked' | 'disliked' | 'upvoted' | 'downvoted' | 'subscribed';
|
|
118
|
+
export interface Tag {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
description: string | null;
|
|
122
|
+
color: string | null;
|
|
123
|
+
createdAt: string;
|
|
124
|
+
updatedAt: string;
|
|
125
|
+
_count?: {
|
|
126
|
+
threads: number;
|
|
127
|
+
};
|
|
128
|
+
threads?: Thread[];
|
|
129
|
+
}
|
|
130
|
+
export interface UserListResponse {
|
|
131
|
+
users: User[];
|
|
132
|
+
nextUserCursor?: string;
|
|
133
|
+
count: number;
|
|
134
|
+
}
|
|
135
|
+
export interface TagListResponse {
|
|
136
|
+
tags: Tag[];
|
|
137
|
+
nextTagCursor?: string;
|
|
138
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@foru-ms/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"prepublishOnly": "npm run build"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"cross-fetch": "^3.1.5"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^4.9.0",
|
|
18
|
+
"@types/node": "^18.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/Client.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AuthResource } from './resources/Auth';
|
|
2
|
+
import { ThreadsResource } from './resources/Threads';
|
|
3
|
+
import { PostsResource } from './resources/Posts';
|
|
4
|
+
import { UsersResource } from './resources/Users';
|
|
5
|
+
import { TagsResource } from './resources/Tags';
|
|
6
|
+
|
|
7
|
+
// Polyfill fetch if needed (e.g. older Node versions)
|
|
8
|
+
const fetch = globalThis.fetch || require('cross-fetch');
|
|
9
|
+
|
|
10
|
+
export class ForumClient {
|
|
11
|
+
public apiKey: string;
|
|
12
|
+
public token: string | null = null;
|
|
13
|
+
public baseUrl: string;
|
|
14
|
+
|
|
15
|
+
public auth: AuthResource;
|
|
16
|
+
public threads: ThreadsResource;
|
|
17
|
+
public posts: PostsResource;
|
|
18
|
+
public users: UsersResource;
|
|
19
|
+
public tags: TagsResource;
|
|
20
|
+
|
|
21
|
+
constructor(options: { apiKey: string; baseUrl?: string }) {
|
|
22
|
+
this.apiKey = options.apiKey;
|
|
23
|
+
this.baseUrl = options.baseUrl || 'https://api.foru.ms/v1';
|
|
24
|
+
|
|
25
|
+
this.auth = new AuthResource(this);
|
|
26
|
+
this.threads = new ThreadsResource(this);
|
|
27
|
+
this.posts = new PostsResource(this);
|
|
28
|
+
this.users = new UsersResource(this);
|
|
29
|
+
this.tags = new TagsResource(this);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
33
|
+
const headers: Record<string, string> = {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'x-api-key': this.apiKey,
|
|
36
|
+
...(options.headers as Record<string, string>),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (this.token) {
|
|
40
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
44
|
+
...options,
|
|
45
|
+
headers,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const contentType = response.headers.get('content-type');
|
|
49
|
+
let data;
|
|
50
|
+
if (contentType && contentType.includes('application/json')) {
|
|
51
|
+
data = await response.json();
|
|
52
|
+
} else {
|
|
53
|
+
data = await response.text();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error((data && (data as any).message) || (data as any).error || 'API Error');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return data as T;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public setToken(token: string) {
|
|
64
|
+
this.token = token;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { LoginResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export class AuthResource {
|
|
5
|
+
private client: ForumClient;
|
|
6
|
+
|
|
7
|
+
constructor(client: ForumClient) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async login(payload: { login: string; password: string; }): Promise<LoginResponse> {
|
|
12
|
+
const response = await this.client.request<LoginResponse>('/auth/login', {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
body: JSON.stringify(payload),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (response.token) {
|
|
18
|
+
this.client.setToken(response.token);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return response;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async register(payload: import('../types').RegisterPayload): Promise<import('../types').User> {
|
|
25
|
+
return this.client.request<import('../types').User>('/auth/register', {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
body: JSON.stringify(payload),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async me(): Promise<import('../types').User> {
|
|
32
|
+
return this.client.request<import('../types').User>('/auth/me', {
|
|
33
|
+
method: 'GET',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { PostListResponse, InteractionType } from '../types';
|
|
3
|
+
|
|
4
|
+
export class PostsResource {
|
|
5
|
+
private client: ForumClient;
|
|
6
|
+
|
|
7
|
+
constructor(client: ForumClient) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async list(params?: {
|
|
12
|
+
query?: string;
|
|
13
|
+
filter?: 'newest' | 'oldest';
|
|
14
|
+
type?: InteractionType;
|
|
15
|
+
cursor?: string;
|
|
16
|
+
userId?: string;
|
|
17
|
+
}): Promise<PostListResponse> {
|
|
18
|
+
const searchParams = new URLSearchParams();
|
|
19
|
+
if (params) {
|
|
20
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
21
|
+
if (value !== undefined) {
|
|
22
|
+
searchParams.append(key, value as string);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.client.request<PostListResponse>(`/posts?${searchParams.toString()}`, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async create(payload: import('../types').CreatePostPayload): Promise<import('../types').Post> {
|
|
33
|
+
return this.client.request<import('../types').Post>('/post', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async retrieve(postId: string): Promise<import('../types').Post> {
|
|
40
|
+
return this.client.request<import('../types').Post>(`/post/${postId}`, {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async update(postId: string, payload: import('../types').UpdatePostPayload): Promise<import('../types').Post> {
|
|
46
|
+
return this.client.request<import('../types').Post>(`/post/${postId}`, {
|
|
47
|
+
method: 'PUT',
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async delete(postId: string, payload?: { userId: string }): Promise<import('../types').Post & { deleted: boolean }> {
|
|
53
|
+
return this.client.request<import('../types').Post & { deleted: boolean }>(`/post/${postId}`, {
|
|
54
|
+
method: 'DELETE',
|
|
55
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { TagListResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export class TagsResource {
|
|
5
|
+
private client: ForumClient;
|
|
6
|
+
|
|
7
|
+
constructor(client: ForumClient) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async list(params?: {
|
|
12
|
+
query?: string;
|
|
13
|
+
cursor?: string;
|
|
14
|
+
}): Promise<TagListResponse> {
|
|
15
|
+
const searchParams = new URLSearchParams();
|
|
16
|
+
if (params) {
|
|
17
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
18
|
+
if (value !== undefined) {
|
|
19
|
+
searchParams.append(key, value as string);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return this.client.request<TagListResponse>(`/tags?${searchParams.toString()}`, {
|
|
25
|
+
method: 'GET',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { ThreadListResponse, ThreadFilter, InteractionType } from '../types';
|
|
3
|
+
|
|
4
|
+
export class ThreadsResource {
|
|
5
|
+
private client: ForumClient;
|
|
6
|
+
|
|
7
|
+
constructor(client: ForumClient) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async list(params?: {
|
|
12
|
+
query?: string;
|
|
13
|
+
tagId?: string;
|
|
14
|
+
filter?: ThreadFilter;
|
|
15
|
+
type?: InteractionType;
|
|
16
|
+
cursor?: string;
|
|
17
|
+
userId?: string;
|
|
18
|
+
}): Promise<ThreadListResponse> {
|
|
19
|
+
const searchParams = new URLSearchParams();
|
|
20
|
+
if (params) {
|
|
21
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
22
|
+
if (value !== undefined) {
|
|
23
|
+
searchParams.append(key, value as string);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return this.client.request<ThreadListResponse>(`/threads?${searchParams.toString()}`, {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async create(payload: import('../types').CreateThreadPayload): Promise<import('../types').Thread> {
|
|
34
|
+
return this.client.request<import('../types').Thread>('/thread', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: JSON.stringify(payload),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async retrieve(threadId: string): Promise<import('../types').Thread> {
|
|
41
|
+
return this.client.request<import('../types').Thread>(`/thread/${threadId}`, {
|
|
42
|
+
method: 'GET',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async update(threadId: string, payload: import('../types').UpdateThreadPayload): Promise<import('../types').Thread> {
|
|
47
|
+
return this.client.request<import('../types').Thread>(`/thread/${threadId}`, {
|
|
48
|
+
method: 'PUT',
|
|
49
|
+
body: JSON.stringify(payload),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async delete(threadId: string, payload?: { userId: string }): Promise<import('../types').Thread & { deleted: boolean }> {
|
|
54
|
+
return this.client.request<import('../types').Thread & { deleted: boolean }>(`/thread/${threadId}`, {
|
|
55
|
+
method: 'DELETE',
|
|
56
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ForumClient } from '../Client';
|
|
2
|
+
import { UserListResponse, User } from '../types';
|
|
3
|
+
|
|
4
|
+
export class UsersResource {
|
|
5
|
+
private client: ForumClient;
|
|
6
|
+
|
|
7
|
+
constructor(client: ForumClient) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async list(params?: {
|
|
12
|
+
query?: string;
|
|
13
|
+
filter?: 'newest' | 'oldest';
|
|
14
|
+
cursor?: string;
|
|
15
|
+
}): Promise<UserListResponse> {
|
|
16
|
+
const searchParams = new URLSearchParams();
|
|
17
|
+
if (params) {
|
|
18
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
19
|
+
if (value !== undefined) {
|
|
20
|
+
searchParams.append(key, value as string);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return this.client.request<UserListResponse>(`/users?${searchParams.toString()}`, {
|
|
26
|
+
method: 'GET',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async retrieve(userId: string): Promise<User> {
|
|
31
|
+
return this.client.request<User>(`/user/${userId}`, {
|
|
32
|
+
method: 'GET',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Auth
|
|
2
|
+
export interface RegisterPayload {
|
|
3
|
+
username: string;
|
|
4
|
+
email: string;
|
|
5
|
+
password: string;
|
|
6
|
+
displayName?: string;
|
|
7
|
+
emailVerified?: boolean;
|
|
8
|
+
roles?: string[];
|
|
9
|
+
extendedData?: Record<string, any>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface User {
|
|
13
|
+
id: string;
|
|
14
|
+
username: string;
|
|
15
|
+
email?: string; // Optional as it might be private in some contexts
|
|
16
|
+
displayName: string | null;
|
|
17
|
+
roles?: any[]; // Refine if needed
|
|
18
|
+
extendedData?: Record<string, any>;
|
|
19
|
+
createdAt?: string;
|
|
20
|
+
updatedAt?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Thread
|
|
24
|
+
export interface Thread {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
slug: string;
|
|
28
|
+
body: string;
|
|
29
|
+
userId: string;
|
|
30
|
+
locked: boolean;
|
|
31
|
+
pinned: boolean;
|
|
32
|
+
createdAt: string;
|
|
33
|
+
updatedAt: string;
|
|
34
|
+
user?: User;
|
|
35
|
+
likes?: any[];
|
|
36
|
+
upvotes?: any[];
|
|
37
|
+
tags?: any[];
|
|
38
|
+
poll?: any;
|
|
39
|
+
extendedData?: Record<string, any>;
|
|
40
|
+
_count?: {
|
|
41
|
+
Post: number;
|
|
42
|
+
likes?: number;
|
|
43
|
+
upvotes?: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CreateThreadPayload {
|
|
48
|
+
title: string;
|
|
49
|
+
body: string;
|
|
50
|
+
userId?: string; // Optional if using token
|
|
51
|
+
slug?: string;
|
|
52
|
+
locked?: boolean;
|
|
53
|
+
pinned?: boolean;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
poll?: {
|
|
56
|
+
title: string;
|
|
57
|
+
options: {
|
|
58
|
+
title: string;
|
|
59
|
+
color?: string;
|
|
60
|
+
extendedData?: Record<string, any>;
|
|
61
|
+
}[];
|
|
62
|
+
};
|
|
63
|
+
extendedData?: Record<string, any>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface UpdateThreadPayload {
|
|
67
|
+
title?: string;
|
|
68
|
+
slug?: string;
|
|
69
|
+
body?: string;
|
|
70
|
+
userId?: string;
|
|
71
|
+
locked?: boolean;
|
|
72
|
+
pinned?: boolean;
|
|
73
|
+
extendedData?: Record<string, any>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Post
|
|
77
|
+
export interface Post {
|
|
78
|
+
id: string;
|
|
79
|
+
body: string;
|
|
80
|
+
threadId: string;
|
|
81
|
+
userId: string;
|
|
82
|
+
parentId?: string | null;
|
|
83
|
+
depth?: number;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
updatedAt: string;
|
|
86
|
+
user?: User;
|
|
87
|
+
Thread?: {
|
|
88
|
+
userId: string;
|
|
89
|
+
title: string;
|
|
90
|
+
};
|
|
91
|
+
extendedData?: Record<string, any>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CreatePostPayload {
|
|
95
|
+
body: string;
|
|
96
|
+
threadId: string;
|
|
97
|
+
userId?: string; // Optional if using token
|
|
98
|
+
parentId?: string;
|
|
99
|
+
extendedData?: Record<string, any>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface UpdatePostPayload {
|
|
103
|
+
body?: string;
|
|
104
|
+
userId?: string;
|
|
105
|
+
threadId?: string;
|
|
106
|
+
parentId?: string;
|
|
107
|
+
bestAnswer?: boolean;
|
|
108
|
+
extendedData?: Record<string, any>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface PaginatedResponse<T> {
|
|
112
|
+
data: T[]; // Generic wrapper if API changes to this format
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ThreadListResponse {
|
|
116
|
+
threads: Thread[];
|
|
117
|
+
nextThreadCursor?: string;
|
|
118
|
+
count: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface PostListResponse {
|
|
122
|
+
posts: Post[];
|
|
123
|
+
nextPostCursor?: string;
|
|
124
|
+
count: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface LoginResponse {
|
|
128
|
+
token: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export type ThreadFilter = 'newest' | 'oldest';
|
|
132
|
+
export type InteractionType = 'created' | 'liked' | 'disliked' | 'upvoted' | 'downvoted' | 'subscribed';
|
|
133
|
+
|
|
134
|
+
export interface Tag {
|
|
135
|
+
id: string;
|
|
136
|
+
name: string;
|
|
137
|
+
description: string | null;
|
|
138
|
+
color: string | null;
|
|
139
|
+
createdAt: string;
|
|
140
|
+
updatedAt: string;
|
|
141
|
+
_count?: {
|
|
142
|
+
threads: number;
|
|
143
|
+
};
|
|
144
|
+
threads?: Thread[];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface UserListResponse {
|
|
148
|
+
users: User[];
|
|
149
|
+
nextUserCursor?: string;
|
|
150
|
+
count: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface TagListResponse {
|
|
154
|
+
tags: Tag[];
|
|
155
|
+
nextTagCursor?: string;
|
|
156
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2019",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"types": [
|
|
11
|
+
"node"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"src/**/*"
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"../node_modules"
|
|
20
|
+
]
|
|
21
|
+
}
|