perspectapi-ts-sdk 1.1.1

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/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "perspectapi-ts-sdk",
3
+ "version": "1.1.1",
4
+ "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/**/*",
17
+ "src/**/*",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23
+ "test": "node scripts/test-runner.js",
24
+ "test:all": "vitest run",
25
+ "test:watch": "node scripts/test-runner.js watch",
26
+ "test:coverage": "node scripts/test-runner.js coverage",
27
+ "test:unit": "node scripts/test-runner.js unit",
28
+ "test:integration": "node scripts/test-runner.js integration",
29
+ "test:types": "node scripts/test-runner.js types",
30
+ "test:ci": "node scripts/test-runner.js ci",
31
+ "lint": "eslint src --ext .ts",
32
+ "typecheck": "tsc --noEmit",
33
+ "prepublishOnly": "npm run build && npm run test:ci"
34
+ },
35
+ "keywords": [
36
+ "perspectapi",
37
+ "sdk",
38
+ "typescript",
39
+ "cloudflare-workers",
40
+ "headless-cms",
41
+ "api-client"
42
+ ],
43
+ "author": "PerspectAPI Team",
44
+ "license": "MIT",
45
+ "dependencies": {
46
+ "zod": "^3.22.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
51
+ "@typescript-eslint/parser": "^6.0.0",
52
+ "@vitest/coverage-v8": "^1.6.1",
53
+ "eslint": "^8.0.0",
54
+ "tsup": "^8.0.0",
55
+ "typescript": "^5.0.0",
56
+ "vitest": "^1.0.0"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ },
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "https://github.com/perspectapi/perspectapi-ts-sdk.git"
64
+ },
65
+ "bugs": {
66
+ "url": "https://github.com/perspectapi/perspectapi-ts-sdk/issues"
67
+ },
68
+ "homepage": "https://github.com/perspectapi/perspectapi-ts-sdk#readme"
69
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * API Keys Management client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ ApiKey,
8
+ CreateApiKeyRequest,
9
+ UpdateApiKeyRequest,
10
+ PaginatedResponse,
11
+ ApiResponse,
12
+ } from '../types';
13
+
14
+ export class ApiKeysClient extends BaseClient {
15
+ constructor(http: any) {
16
+ super(http, '/api/v1');
17
+ }
18
+
19
+ /**
20
+ * Get all API keys
21
+ */
22
+ async getApiKeys(params?: { page?: number; limit?: number }): Promise<PaginatedResponse<ApiKey>> {
23
+ return this.getPaginated<ApiKey>('/keys', params);
24
+ }
25
+
26
+ /**
27
+ * Get API key by ID
28
+ */
29
+ async getApiKeyById(id: string): Promise<ApiResponse<ApiKey>> {
30
+ return this.getSingle<ApiKey>(`/keys/${id}`);
31
+ }
32
+
33
+ /**
34
+ * Create new API key
35
+ */
36
+ async createApiKey(data: CreateApiKeyRequest): Promise<ApiResponse<ApiKey & { key: string }>> {
37
+ return this.create<CreateApiKeyRequest, ApiKey & { key: string }>('/keys', data);
38
+ }
39
+
40
+ /**
41
+ * Update API key
42
+ */
43
+ async updateApiKey(id: string, data: UpdateApiKeyRequest): Promise<ApiResponse<ApiKey>> {
44
+ return this.update<UpdateApiKeyRequest, ApiKey>(`/keys/${id}`, data);
45
+ }
46
+
47
+ /**
48
+ * Delete API key
49
+ */
50
+ async deleteApiKey(id: string): Promise<ApiResponse<{ message: string }>> {
51
+ return this.delete<{ message: string }>(`/keys/${id}`);
52
+ }
53
+
54
+ /**
55
+ * Regenerate API key
56
+ */
57
+ async regenerateApiKey(id: string): Promise<ApiResponse<ApiKey & { key: string }>> {
58
+ return this.create<Record<string, never>, ApiKey & { key: string }>(`/keys/${id}/regenerate`, {});
59
+ }
60
+
61
+ /**
62
+ * Activate API key
63
+ */
64
+ async activateApiKey(id: string): Promise<ApiResponse<ApiKey>> {
65
+ return this.patch<{ isActive: boolean }, ApiKey>(`/keys/${id}`, { isActive: true });
66
+ }
67
+
68
+ /**
69
+ * Deactivate API key
70
+ */
71
+ async deactivateApiKey(id: string): Promise<ApiResponse<ApiKey>> {
72
+ return this.patch<{ isActive: boolean }, ApiKey>(`/keys/${id}`, { isActive: false });
73
+ }
74
+
75
+ /**
76
+ * Get API key usage statistics
77
+ */
78
+ async getApiKeyStats(id: string): Promise<ApiResponse<{
79
+ totalRequests: number;
80
+ requestsToday: number;
81
+ requestsThisMonth: number;
82
+ lastUsedAt?: string;
83
+ }>> {
84
+ return this.getSingle(`/keys/${id}/stats`);
85
+ }
86
+
87
+ /**
88
+ * Test API key validity
89
+ */
90
+ async testApiKey(key: string): Promise<ApiResponse<{
91
+ valid: boolean;
92
+ keyId?: string;
93
+ permissions?: string[];
94
+ expiresAt?: string;
95
+ }>> {
96
+ return this.create<{ key: string }, {
97
+ valid: boolean;
98
+ keyId?: string;
99
+ permissions?: string[];
100
+ expiresAt?: string;
101
+ }>('/keys/test', { key });
102
+ }
103
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Authentication client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ SignUpRequest,
8
+ SignInRequest,
9
+ AuthResponse,
10
+ User,
11
+ ApiResponse,
12
+ } from '../types';
13
+
14
+ export class AuthClient extends BaseClient {
15
+ constructor(http: any) {
16
+ super(http, '/api/v1');
17
+ }
18
+
19
+ /**
20
+ * Get CSRF token
21
+ */
22
+ async getCsrfToken(): Promise<ApiResponse<{ token: string }>> {
23
+ return this.http.get<{ token: string }>(this.buildPath('/csrf'));
24
+ }
25
+
26
+ /**
27
+ * Sign up a new user
28
+ */
29
+ async signUp(data: SignUpRequest): Promise<ApiResponse<{ message: string; userId: string }>> {
30
+ return this.create<SignUpRequest, { message: string; userId: string }>('/signup', data);
31
+ }
32
+
33
+ /**
34
+ * Sign in user
35
+ */
36
+ async signIn(data: SignInRequest): Promise<ApiResponse<AuthResponse>> {
37
+ const response = await this.create<SignInRequest, AuthResponse>('/authenticate', data);
38
+
39
+ // Update HTTP client with JWT if provided
40
+ if (response.data && 'token' in response.data && response.data.token) {
41
+ this.http.setAuth(response.data.token);
42
+ }
43
+
44
+ return response;
45
+ }
46
+
47
+ /**
48
+ * Activate user account
49
+ */
50
+ async activateAccount(activationKey: string): Promise<ApiResponse<{ message: string }>> {
51
+ return this.getSingle<{ message: string }>(`/activate/${activationKey}`);
52
+ }
53
+
54
+ /**
55
+ * Request password reset
56
+ */
57
+ async forgotPassword(email: string): Promise<ApiResponse<{ message: string }>> {
58
+ return this.create<{ email: string }, { message: string }>('/forgotpassword', { email });
59
+ }
60
+
61
+ /**
62
+ * Reset password with token
63
+ */
64
+ async resetPassword(
65
+ resetToken: string,
66
+ password: string
67
+ ): Promise<ApiResponse<{ message: string }>> {
68
+ return this.create<{ password: string }, { message: string }>(
69
+ `/passwordreset/${resetToken}`,
70
+ { password }
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Get current user profile
76
+ */
77
+ async getUserProfile(): Promise<ApiResponse<{ user: User }>> {
78
+ return this.getSingle<{ user: User }>('/userprofile');
79
+ }
80
+
81
+ /**
82
+ * Sign out (clear local auth)
83
+ */
84
+ signOut(): void {
85
+ this.http.clearAuth();
86
+ }
87
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Base client class for PerspectAPI SDK
3
+ */
4
+
5
+ import { HttpClient } from '../utils/http-client';
6
+ import type { ApiResponse } from '../types';
7
+
8
+ export abstract class BaseClient {
9
+ protected http: HttpClient;
10
+ protected basePath: string;
11
+
12
+ constructor(http: HttpClient, basePath: string) {
13
+ this.http = http;
14
+ this.basePath = basePath;
15
+ }
16
+
17
+ /**
18
+ * Build a site-scoped endpoint relative to the API base path
19
+ */
20
+ protected siteScopedEndpoint(
21
+ siteName: string,
22
+ endpoint = '',
23
+ options?: { includeSitesSegment?: boolean }
24
+ ): string {
25
+ if (!siteName || !siteName.trim()) {
26
+ throw new Error('siteName is required');
27
+ }
28
+
29
+ const encodedSiteName = encodeURIComponent(siteName.trim());
30
+ const cleanEndpoint = endpoint.replace(/^\//, '');
31
+ const includeSitesSegment = options?.includeSitesSegment ?? true;
32
+ const baseSegment = includeSitesSegment ? `sites/${encodedSiteName}` : encodedSiteName;
33
+ const suffix = cleanEndpoint ? `/${cleanEndpoint}` : '';
34
+
35
+ return `/${baseSegment}${suffix}`;
36
+ }
37
+
38
+ /**
39
+ * Build endpoint path
40
+ */
41
+ protected buildPath(endpoint: string): string {
42
+ const cleanBasePath = this.basePath.replace(/\/$/, '');
43
+ const cleanEndpoint = endpoint.replace(/^\//, '');
44
+ return `${cleanBasePath}/${cleanEndpoint}`;
45
+ }
46
+
47
+ /**
48
+ * Handle paginated responses
49
+ */
50
+ protected async getPaginated<T>(
51
+ endpoint: string,
52
+ params?: Record<string, any>
53
+ ): Promise<ApiResponse<T[]>> {
54
+ return this.http.get<T[]>(this.buildPath(endpoint), params);
55
+ }
56
+
57
+ /**
58
+ * Handle single resource responses
59
+ */
60
+ protected async getSingle<T>(endpoint: string): Promise<ApiResponse<T>> {
61
+ return this.http.get<T>(this.buildPath(endpoint));
62
+ }
63
+
64
+ /**
65
+ * Handle create operations
66
+ */
67
+ protected async create<T, R = T>(
68
+ endpoint: string,
69
+ data: T
70
+ ): Promise<ApiResponse<R>> {
71
+ return this.http.post<R>(this.buildPath(endpoint), data);
72
+ }
73
+
74
+ /**
75
+ * Handle update operations
76
+ */
77
+ protected async update<T, R = T>(
78
+ endpoint: string,
79
+ data: T
80
+ ): Promise<ApiResponse<R>> {
81
+ return this.http.put<R>(this.buildPath(endpoint), data);
82
+ }
83
+
84
+ /**
85
+ * Handle partial update operations
86
+ */
87
+ protected async patch<T, R = T>(
88
+ endpoint: string,
89
+ data: T
90
+ ): Promise<ApiResponse<R>> {
91
+ return this.http.patch<R>(this.buildPath(endpoint), data);
92
+ }
93
+
94
+ /**
95
+ * Handle delete operations
96
+ */
97
+ protected async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
98
+ return this.http.delete<T>(this.buildPath(endpoint));
99
+ }
100
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Categories Management client for PerspectAPI SDK
3
+ */
4
+
5
+ import { BaseClient } from './base-client';
6
+ import type {
7
+ Category,
8
+ CreateCategoryRequest,
9
+ PaginatedResponse,
10
+ ApiResponse,
11
+ } from '../types';
12
+
13
+ export class CategoriesClient extends BaseClient {
14
+ constructor(http: any) {
15
+ super(http, '/api/v1');
16
+ }
17
+
18
+ /**
19
+ * Get all categories
20
+ */
21
+ async getCategories(params?: {
22
+ page?: number;
23
+ limit?: number;
24
+ parentId?: number;
25
+ organizationId?: number;
26
+ }): Promise<PaginatedResponse<Category>> {
27
+ return this.getPaginated<Category>('/categories', params);
28
+ }
29
+
30
+ /**
31
+ * Get category by ID
32
+ */
33
+ async getCategoryById(id: number): Promise<ApiResponse<Category>> {
34
+ return this.getSingle<Category>(`/categories/${id}`);
35
+ }
36
+
37
+ /**
38
+ * Get category by slug
39
+ */
40
+ async getCategoryBySlug(slug: string): Promise<ApiResponse<Category>> {
41
+ return this.getSingle<Category>(`/categories/slug/${slug}`);
42
+ }
43
+
44
+ /**
45
+ * Get product category by slug (for products)
46
+ */
47
+ async getProductCategoryBySlug(siteName: string, slug: string): Promise<ApiResponse<Category>> {
48
+ return this.http.get(this.buildPath(
49
+ this.siteScopedEndpoint(siteName, `/product_category/slug/${encodeURIComponent(slug)}`)
50
+ ));
51
+ }
52
+
53
+ /**
54
+ * Create new category
55
+ */
56
+ async createCategory(data: CreateCategoryRequest): Promise<ApiResponse<Category>> {
57
+ return this.create<CreateCategoryRequest, Category>('/categories', data);
58
+ }
59
+
60
+ /**
61
+ * Update category
62
+ */
63
+ async updateCategory(id: number, data: Partial<CreateCategoryRequest>): Promise<ApiResponse<Category>> {
64
+ return this.update<Partial<CreateCategoryRequest>, Category>(`/categories/${id}`, data);
65
+ }
66
+
67
+ /**
68
+ * Delete category
69
+ */
70
+ async deleteCategory(id: number): Promise<ApiResponse<{ message: string }>> {
71
+ return this.delete<{ message: string }>(`/categories/${id}`);
72
+ }
73
+
74
+ /**
75
+ * Get category tree (hierarchical structure)
76
+ */
77
+ async getCategoryTree(rootId?: number): Promise<ApiResponse<Array<Category & {
78
+ children: Category[];
79
+ }>>> {
80
+ const endpoint = rootId ? `/categories/tree/${rootId}` : '/categories/tree';
81
+ return this.getSingle(endpoint);
82
+ }
83
+
84
+ /**
85
+ * Get category children
86
+ */
87
+ async getCategoryChildren(id: number): Promise<ApiResponse<Category[]>> {
88
+ return this.getSingle<Category[]>(`/categories/${id}/children`);
89
+ }
90
+
91
+ /**
92
+ * Get category parent
93
+ */
94
+ async getCategoryParent(id: number): Promise<ApiResponse<Category | null>> {
95
+ return this.getSingle<Category | null>(`/categories/${id}/parent`);
96
+ }
97
+
98
+ /**
99
+ * Move category to new parent
100
+ */
101
+ async moveCategoryToParent(id: number, parentId: number | null): Promise<ApiResponse<Category>> {
102
+ return this.patch<{ parentId: number | null }, Category>(`/categories/${id}`, { parentId });
103
+ }
104
+
105
+ /**
106
+ * Get category breadcrumb path
107
+ */
108
+ async getCategoryBreadcrumb(id: number): Promise<ApiResponse<Array<{
109
+ id: number;
110
+ name: string;
111
+ slug: string;
112
+ }>>> {
113
+ return this.getSingle(`/categories/${id}/breadcrumb`);
114
+ }
115
+
116
+ /**
117
+ * Get category content/products
118
+ */
119
+ async getCategoryContent(id: number, params?: {
120
+ page?: number;
121
+ limit?: number;
122
+ type?: 'content' | 'products' | 'all';
123
+ }): Promise<ApiResponse<{
124
+ content: any[];
125
+ products: any[];
126
+ total: number;
127
+ }>> {
128
+ return this.http.get(`/categories/${id}/content`, params);
129
+ }
130
+
131
+ /**
132
+ * Bulk update category order
133
+ */
134
+ async updateCategoryOrder(updates: Array<{
135
+ id: number;
136
+ order: number;
137
+ parentId?: number;
138
+ }>): Promise<ApiResponse<{ message: string }>> {
139
+ return this.create('/categories/reorder', { updates });
140
+ }
141
+
142
+ /**
143
+ * Search categories
144
+ */
145
+ async searchCategories(query: string, params?: {
146
+ limit?: number;
147
+ organizationId?: number;
148
+ }): Promise<ApiResponse<Category[]>> {
149
+ return this.http.get(`/categories/search`, { q: query, ...params });
150
+ }
151
+ }