@mybe/sdk 1.0.2 → 1.0.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/README.md +546 -366
- package/dist/client.d.ts +87 -0
- package/dist/client.js +169 -3
- package/dist/types.d.ts +10 -0
- package/package.json +24 -24
package/README.md
CHANGED
|
@@ -1,366 +1,546 @@
|
|
|
1
|
-
# Mybe CMS SDK
|
|
2
|
-
|
|
3
|
-
A TypeScript SDK for fetching content from Mybe CMS.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @mybe/sdk
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
- ✅ **Zero Dependencies** - Uses native fetch API
|
|
14
|
-
- ✅ **TypeScript Support** - Full type safety with TypeScript
|
|
15
|
-
- ✅ **Auto Environment Detection** - Automatically detects development vs production
|
|
16
|
-
- ✅ **Error Handling** - Custom error classes for better error handling
|
|
17
|
-
- ✅ **Pagination Support** - Built-in pagination for large datasets
|
|
18
|
-
- ✅ **Status Filtering** - Filter content by status (draft, published, archived)
|
|
19
|
-
- ✅ **Locale Filtering** - Filter content by locale for multi-language support
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- **
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
console.log(result.
|
|
115
|
-
console.log(result.pagination.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- `
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
- `
|
|
140
|
-
- `
|
|
141
|
-
- `
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
- `
|
|
202
|
-
- `
|
|
203
|
-
- `
|
|
204
|
-
- `
|
|
205
|
-
- `
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
console.log(post.data.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
console.log(content.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
console.log(item.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
- `
|
|
351
|
-
- `
|
|
352
|
-
- `
|
|
353
|
-
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
1
|
+
# Mybe CMS SDK
|
|
2
|
+
|
|
3
|
+
A TypeScript SDK for fetching content from Mybe CMS.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mybe/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- ✅ **Zero Dependencies** - Uses native fetch API
|
|
14
|
+
- ✅ **TypeScript Support** - Full type safety with TypeScript
|
|
15
|
+
- ✅ **Auto Environment Detection** - Automatically detects development vs production
|
|
16
|
+
- ✅ **Error Handling** - Custom error classes for better error handling
|
|
17
|
+
- ✅ **Pagination Support** - Built-in pagination for large datasets
|
|
18
|
+
- ✅ **Status Filtering** - Filter content by status (draft, published, archived)
|
|
19
|
+
- ✅ **Locale Filtering** - Filter content by locale for multi-language support
|
|
20
|
+
- ✅ **GraphQL Support** - Powerful GraphQL API for flexible data querying
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { MybeSDK } from '@mybe/sdk';
|
|
26
|
+
|
|
27
|
+
// Initialize the SDK
|
|
28
|
+
const sdk = new MybeSDK({
|
|
29
|
+
apiKey: 'your-api-key'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Fetch a single content entry
|
|
33
|
+
const content = await sdk.getContent('content-id');
|
|
34
|
+
|
|
35
|
+
// Fetch content by type with filters
|
|
36
|
+
const contentList = await sdk.getContentByType('content-type-id', {
|
|
37
|
+
status: 'published',
|
|
38
|
+
limit: 10
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
### Basic Configuration
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const sdk = new MybeSDK({
|
|
48
|
+
apiKey: 'your-api-key'
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The SDK automatically detects the environment:
|
|
53
|
+
- **Development** (`NODE_ENV=development`): Uses `https://sdk-dev.contensa.ai/dev/api/v1`
|
|
54
|
+
- **Production**: Uses `https://sdk-dev.contensa.ai/dev/api/v1`
|
|
55
|
+
|
|
56
|
+
> **Note:** The SDK uses a custom domain for all environments, providing a professional branded URL while hiding the underlying AWS infrastructure. Both development and production currently point to the same endpoint temporarily.
|
|
57
|
+
|
|
58
|
+
### Custom Base URL (Optional)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const sdk = new MybeSDK({
|
|
62
|
+
apiKey: 'your-api-key',
|
|
63
|
+
baseUrl: 'https://custom-api.example.com/api/v1'
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### Content Models (Content Types)
|
|
70
|
+
|
|
71
|
+
#### `getContentModels(projectId: string)`
|
|
72
|
+
|
|
73
|
+
Get all content models for a project.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const contentModels = await sdk.getContentModels('project-id');
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Returns:** `Promise<ContentType[]>`
|
|
80
|
+
|
|
81
|
+
#### `getContentModel(contentTypeId: string)`
|
|
82
|
+
|
|
83
|
+
Get a specific content model by ID.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const contentModel = await sdk.getContentModel('content-type-id');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Returns:** `Promise<ContentType>`
|
|
90
|
+
|
|
91
|
+
### Content Entries
|
|
92
|
+
|
|
93
|
+
#### `getContent(contentId: string)`
|
|
94
|
+
|
|
95
|
+
Get a single content entry by ID.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const content = await sdk.getContent('content-id');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Returns:** `Promise<ContentEntry>`
|
|
102
|
+
|
|
103
|
+
#### `getContentByType(contentTypeId: string, options?: ContentFilterOptions)`
|
|
104
|
+
|
|
105
|
+
Get all content entries for a specific content type.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const result = await sdk.getContentByType('content-type-id', {
|
|
109
|
+
status: 'published',
|
|
110
|
+
limit: 20,
|
|
111
|
+
lastKey: 'pagination-key' // For pagination
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log(result.data); // Array of content entries
|
|
115
|
+
console.log(result.pagination.hasMore); // Boolean
|
|
116
|
+
console.log(result.pagination.lastEvaluatedKey); // For next page
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Options:**
|
|
120
|
+
- `status?: 'draft' | 'published' | 'archived'` - Filter by status
|
|
121
|
+
- `locale?: string` - Filter by locale (e.g., 'en-US', 'bn-BD', 'fr-FR', 'es-ES')
|
|
122
|
+
- `limit?: number` - Number of items per page
|
|
123
|
+
- `lastKey?: string` - Pagination key from previous response
|
|
124
|
+
|
|
125
|
+
**Returns:** `Promise<ContentListResponse>`
|
|
126
|
+
|
|
127
|
+
#### `getContentByProject(projectId: string, options?: ContentFilterOptions)`
|
|
128
|
+
|
|
129
|
+
Get all content entries for a project.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const result = await sdk.getContentByProject('project-id', {
|
|
133
|
+
status: 'published',
|
|
134
|
+
limit: 20
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Options:**
|
|
139
|
+
- `status?: 'draft' | 'published' | 'archived'` - Filter by status
|
|
140
|
+
- `locale?: string` - Filter by locale (e.g., 'en-US', 'bn-BD', 'fr-FR', 'es-ES')
|
|
141
|
+
- `limit?: number` - Number of items per page
|
|
142
|
+
- `lastKey?: string` - Pagination key from previous response
|
|
143
|
+
|
|
144
|
+
**Returns:** `Promise<ContentListResponse>`
|
|
145
|
+
|
|
146
|
+
## Pagination Example
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
let lastKey: string | undefined;
|
|
150
|
+
let allContent: ContentEntry[] = [];
|
|
151
|
+
|
|
152
|
+
do {
|
|
153
|
+
const result = await sdk.getContentByType('content-type-id', {
|
|
154
|
+
status: 'published',
|
|
155
|
+
limit: 50,
|
|
156
|
+
lastKey: lastKey ? JSON.stringify(result.pagination.lastEvaluatedKey) : undefined
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
allContent = [...allContent, ...result.data];
|
|
160
|
+
lastKey = result.pagination.hasMore
|
|
161
|
+
? JSON.stringify(result.pagination.lastEvaluatedKey)
|
|
162
|
+
: undefined;
|
|
163
|
+
|
|
164
|
+
} while (lastKey);
|
|
165
|
+
|
|
166
|
+
console.log(`Fetched ${allContent.length} total items`);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Error Handling
|
|
170
|
+
|
|
171
|
+
The SDK provides custom error classes for better error handling:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import {
|
|
175
|
+
MybeSDK,
|
|
176
|
+
NotFoundError,
|
|
177
|
+
UnauthorizedError,
|
|
178
|
+
ValidationError,
|
|
179
|
+
ServerError
|
|
180
|
+
} from '@mybe/sdk';
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const content = await sdk.getContent('content-id');
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof NotFoundError) {
|
|
186
|
+
console.error('Content not found');
|
|
187
|
+
} else if (error instanceof UnauthorizedError) {
|
|
188
|
+
console.error('Invalid API key');
|
|
189
|
+
} else if (error instanceof ValidationError) {
|
|
190
|
+
console.error('Validation error:', error.message);
|
|
191
|
+
} else if (error instanceof ServerError) {
|
|
192
|
+
console.error('Server error:', error.message);
|
|
193
|
+
} else {
|
|
194
|
+
console.error('Unknown error:', error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Error Types
|
|
200
|
+
|
|
201
|
+
- `MybeSDKError` - Base error class
|
|
202
|
+
- `NotFoundError` - Resource not found (404)
|
|
203
|
+
- `UnauthorizedError` - Invalid API key (401)
|
|
204
|
+
- `ForbiddenError` - Insufficient permissions (403)
|
|
205
|
+
- `ValidationError` - Validation failed (400)
|
|
206
|
+
- `ServerError` - Internal server error (500)
|
|
207
|
+
|
|
208
|
+
## TypeScript Types
|
|
209
|
+
|
|
210
|
+
The SDK exports all TypeScript types for your convenience:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import type {
|
|
214
|
+
ContentType,
|
|
215
|
+
ContentEntry,
|
|
216
|
+
ContentFilterOptions,
|
|
217
|
+
PaginationResponse,
|
|
218
|
+
APIResponse,
|
|
219
|
+
ContentListResponse
|
|
220
|
+
} from '@mybe/sdk';
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### ContentType
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
interface ContentType {
|
|
227
|
+
id: string;
|
|
228
|
+
project_id: string;
|
|
229
|
+
name: string;
|
|
230
|
+
slug: string;
|
|
231
|
+
description?: string;
|
|
232
|
+
created_at: string;
|
|
233
|
+
updated_at: string;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### ContentEntry
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
interface ContentEntry {
|
|
241
|
+
id: string;
|
|
242
|
+
content_type_id: string;
|
|
243
|
+
project_id: string;
|
|
244
|
+
slug?: string;
|
|
245
|
+
status: 'draft' | 'published' | 'archived';
|
|
246
|
+
data: Record<string, any>;
|
|
247
|
+
locale?: string;
|
|
248
|
+
created_by: string;
|
|
249
|
+
updated_by?: string;
|
|
250
|
+
published_at?: string;
|
|
251
|
+
created_at: string;
|
|
252
|
+
updated_at: string;
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Examples
|
|
257
|
+
|
|
258
|
+
### Fetch Published Blog Posts
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const sdk = new MybeSDK({ apiKey: 'your-api-key' });
|
|
262
|
+
|
|
263
|
+
const posts = await sdk.getContentByType('blog-post-type-id', {
|
|
264
|
+
status: 'published',
|
|
265
|
+
limit: 10
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
posts.data.forEach(post => {
|
|
269
|
+
console.log(post.data.title);
|
|
270
|
+
console.log(post.data.content);
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Fetch All Content Models
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const contentModels = await sdk.getContentModels('project-id');
|
|
278
|
+
|
|
279
|
+
contentModels.forEach(model => {
|
|
280
|
+
console.log(`${model.name} (${model.slug})`);
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Fetch Single Content Entry
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
const content = await sdk.getContent('content-entry-id');
|
|
288
|
+
|
|
289
|
+
console.log(content.data); // Your content data
|
|
290
|
+
console.log(content.status); // draft | published | archived
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Multi-locale Content
|
|
294
|
+
|
|
295
|
+
Fetch content in different languages using the locale filter:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
const sdk = new MybeSDK({ apiKey: 'your-api-key' });
|
|
299
|
+
|
|
300
|
+
// Fetch English content
|
|
301
|
+
const englishPosts = await sdk.getContentByType('blog-post-type-id', {
|
|
302
|
+
status: 'published',
|
|
303
|
+
locale: 'en-US',
|
|
304
|
+
limit: 10
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Fetch Bangla content
|
|
308
|
+
const banglaPosts = await sdk.getContentByType('blog-post-type-id', {
|
|
309
|
+
status: 'published',
|
|
310
|
+
locale: 'bn-BD',
|
|
311
|
+
limit: 10
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Fetch French content
|
|
315
|
+
const frenchPosts = await sdk.getContentByType('blog-post-type-id', {
|
|
316
|
+
status: 'published',
|
|
317
|
+
locale: 'fr-FR',
|
|
318
|
+
limit: 10
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Fetch Spanish content
|
|
322
|
+
const spanishPosts = await sdk.getContentByType('blog-post-type-id', {
|
|
323
|
+
status: 'published',
|
|
324
|
+
locale: 'es-ES',
|
|
325
|
+
limit: 10
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Building a Multi-language Website:**
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// Get user's preferred language
|
|
333
|
+
const userLocale = getUserPreferredLocale(); // e.g., 'bn-BD'
|
|
334
|
+
|
|
335
|
+
// Fetch content in user's language
|
|
336
|
+
const localizedContent = await sdk.getContentByType('content-type-id', {
|
|
337
|
+
status: 'published',
|
|
338
|
+
locale: userLocale,
|
|
339
|
+
limit: 20
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Display localized content
|
|
343
|
+
localizedContent.data.forEach(item => {
|
|
344
|
+
console.log(item.data.title); // Title in user's language
|
|
345
|
+
console.log(item.metadata.locale); // e.g., 'bn-BD'
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Supported Locales:**
|
|
350
|
+
- `en-US` - English (United States)
|
|
351
|
+
- `bn-BD` - Bangla (Bangladesh)
|
|
352
|
+
- `fr-FR` - French (France)
|
|
353
|
+
- `es-ES` - Spanish (Spain)
|
|
354
|
+
- Custom locales as configured in your CMS
|
|
355
|
+
|
|
356
|
+
## GraphQL API
|
|
357
|
+
|
|
358
|
+
Mybe CMS provides a powerful GraphQL API for flexible data querying. Use GraphQL when you need advanced filtering, want to request specific fields only, or prefer the GraphQL query language.
|
|
359
|
+
|
|
360
|
+
### Quick Start with GraphQL
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { MybeSDK } from '@mybe/sdk';
|
|
364
|
+
|
|
365
|
+
const sdk = new MybeSDK({ apiKey: 'your-api-key' });
|
|
366
|
+
const PROJECT_ID = 'your-project-id';
|
|
367
|
+
|
|
368
|
+
// Execute a GraphQL query
|
|
369
|
+
const result = await sdk.graphql(PROJECT_ID, `
|
|
370
|
+
query {
|
|
371
|
+
blogPostCollection(limit: 10, where: { status: "published" }) {
|
|
372
|
+
items {
|
|
373
|
+
id
|
|
374
|
+
slug
|
|
375
|
+
data
|
|
376
|
+
}
|
|
377
|
+
total
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
`);
|
|
381
|
+
|
|
382
|
+
console.log(result.blogPostCollection.items);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### GraphQL Method
|
|
386
|
+
|
|
387
|
+
#### `graphql<T>(projectId: string, query: string, variables?: Record<string, any>)`
|
|
388
|
+
|
|
389
|
+
Execute a GraphQL query against your project.
|
|
390
|
+
|
|
391
|
+
**Parameters:**
|
|
392
|
+
- `projectId` - Your project ID
|
|
393
|
+
- `query` - GraphQL query string
|
|
394
|
+
- `variables` - Optional query variables (recommended for dynamic queries)
|
|
395
|
+
|
|
396
|
+
**Returns:** `Promise<T>` - The data from your GraphQL query
|
|
397
|
+
|
|
398
|
+
### GraphQL Examples
|
|
399
|
+
|
|
400
|
+
#### Basic Collection Query
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const { blogPostCollection } = await sdk.graphql(PROJECT_ID, `
|
|
404
|
+
query {
|
|
405
|
+
blogPostCollection(limit: 5) {
|
|
406
|
+
items {
|
|
407
|
+
id
|
|
408
|
+
slug
|
|
409
|
+
status
|
|
410
|
+
data
|
|
411
|
+
published_at
|
|
412
|
+
}
|
|
413
|
+
total
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
`);
|
|
417
|
+
|
|
418
|
+
console.log(`Total posts: ${blogPostCollection.total}`);
|
|
419
|
+
blogPostCollection.items.forEach(post => {
|
|
420
|
+
console.log(post.data.title);
|
|
421
|
+
});
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
#### Filter by Status
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
const { blogPostCollection } = await sdk.graphql(PROJECT_ID, `
|
|
428
|
+
query {
|
|
429
|
+
blogPostCollection(where: { status: "published" }) {
|
|
430
|
+
items {
|
|
431
|
+
id
|
|
432
|
+
slug
|
|
433
|
+
data
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
`);
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
#### Get Single Entry by ID
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
const { blogPost } = await sdk.graphql(PROJECT_ID, `
|
|
444
|
+
query {
|
|
445
|
+
blogPost(id: "entry-123") {
|
|
446
|
+
id
|
|
447
|
+
slug
|
|
448
|
+
status
|
|
449
|
+
data
|
|
450
|
+
created_at
|
|
451
|
+
published_at
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
`);
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
#### Using Variables (Recommended)
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
const result = await sdk.graphql(
|
|
461
|
+
PROJECT_ID,
|
|
462
|
+
`
|
|
463
|
+
query GetPostBySlug($slug: String!) {
|
|
464
|
+
blogPostCollection(where: { slug: $slug }) {
|
|
465
|
+
items {
|
|
466
|
+
id
|
|
467
|
+
slug
|
|
468
|
+
data
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
`,
|
|
473
|
+
{ slug: 'my-blog-post' }
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
const post = result.blogPostCollection.items[0];
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### Pagination with GraphQL
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
// Page 1
|
|
483
|
+
const page1 = await sdk.graphql(PROJECT_ID, `
|
|
484
|
+
query {
|
|
485
|
+
blogPostCollection(limit: 10, skip: 0) {
|
|
486
|
+
items { id slug }
|
|
487
|
+
total
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
`);
|
|
491
|
+
|
|
492
|
+
// Page 2
|
|
493
|
+
const page2 = await sdk.graphql(PROJECT_ID, `
|
|
494
|
+
query {
|
|
495
|
+
blogPostCollection(limit: 10, skip: 10) {
|
|
496
|
+
items { id slug }
|
|
497
|
+
total
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
`);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### REST vs GraphQL - When to Use
|
|
504
|
+
|
|
505
|
+
**Use REST When:**
|
|
506
|
+
- ✅ Simple queries (get by ID, get all)
|
|
507
|
+
- ✅ You want straightforward API calls
|
|
508
|
+
- ✅ You prefer SDK helper methods
|
|
509
|
+
- ✅ You need full object responses
|
|
510
|
+
|
|
511
|
+
**Use GraphQL When:**
|
|
512
|
+
- ✅ Complex filtering requirements
|
|
513
|
+
- ✅ You need specific fields only (reduce payload size)
|
|
514
|
+
- ✅ Multiple queries in one request
|
|
515
|
+
- ✅ You want flexible, ad-hoc queries
|
|
516
|
+
- ✅ Your frontend uses Apollo Client or similar
|
|
517
|
+
|
|
518
|
+
### Complete GraphQL Guide
|
|
519
|
+
|
|
520
|
+
For comprehensive GraphQL documentation, examples, and best practices, see:
|
|
521
|
+
- **[GraphQL API Guide](./GRAPHQL_GUIDE.md)** - Complete GraphQL documentation
|
|
522
|
+
- **[Test Examples](./test-graphql.ts)** - Working code examples
|
|
523
|
+
|
|
524
|
+
### GraphQL Schema
|
|
525
|
+
|
|
526
|
+
Your GraphQL schema is automatically generated from your content types. For example:
|
|
527
|
+
|
|
528
|
+
**Content Type:** "Blog Post"
|
|
529
|
+
**Generates:**
|
|
530
|
+
- `blogPost(id: ID!)` - Get single entry
|
|
531
|
+
- `blogPostCollection(limit, skip, where)` - Get collection
|
|
532
|
+
|
|
533
|
+
**Naming:** Content type names are converted to camelCase (e.g., "Blog Post" → `blogPost`)
|
|
534
|
+
|
|
535
|
+
## Requirements
|
|
536
|
+
|
|
537
|
+
- Node.js 18+ (for native fetch support)
|
|
538
|
+
- TypeScript 5.0+ (for development)
|
|
539
|
+
|
|
540
|
+
## License
|
|
541
|
+
|
|
542
|
+
MIT
|
|
543
|
+
|
|
544
|
+
## Support
|
|
545
|
+
|
|
546
|
+
For issues and questions, please visit the [GitHub repository](https://github.com/MyBeeInovationLabs/mybe-cms).
|
package/dist/client.d.ts
CHANGED
|
@@ -7,6 +7,32 @@ export declare class MybeSDK {
|
|
|
7
7
|
* Make HTTP request to API
|
|
8
8
|
*/
|
|
9
9
|
private request;
|
|
10
|
+
/**
|
|
11
|
+
* Convert kebab-case to camelCase for GraphQL field names
|
|
12
|
+
* Examples:
|
|
13
|
+
* "blog-post" → "blogPost"
|
|
14
|
+
* "case-study" → "caseStudy"
|
|
15
|
+
* "product" → "product" (unchanged)
|
|
16
|
+
*/
|
|
17
|
+
private toCamelCase;
|
|
18
|
+
/**
|
|
19
|
+
* Automatically convert kebab-case content type slugs to camelCase in GraphQL queries
|
|
20
|
+
* This allows users to use their actual content type slugs (e.g., "blog-post")
|
|
21
|
+
* without needing to know about GraphQL's camelCase requirement
|
|
22
|
+
*
|
|
23
|
+
* Converts patterns like:
|
|
24
|
+
* "blog-post" → "blogPost"
|
|
25
|
+
* "blog-postCollection" → "blogPostCollection"
|
|
26
|
+
* "case-study(" → "caseStudy("
|
|
27
|
+
*
|
|
28
|
+
* @returns Object with converted query and mapping of original to converted field names
|
|
29
|
+
*/
|
|
30
|
+
private convertQuerySlugs;
|
|
31
|
+
/**
|
|
32
|
+
* Convert response keys back to the original format used in the query
|
|
33
|
+
* This allows users to access response fields using the same kebab-case format they used in the query
|
|
34
|
+
*/
|
|
35
|
+
private convertResponseKeys;
|
|
10
36
|
/**
|
|
11
37
|
* Build query string from options
|
|
12
38
|
*/
|
|
@@ -31,4 +57,65 @@ export declare class MybeSDK {
|
|
|
31
57
|
* Get all content entries for a project
|
|
32
58
|
*/
|
|
33
59
|
getContentByProject(projectId: string, options?: ContentFilterOptions): Promise<ContentListResponse>;
|
|
60
|
+
/**
|
|
61
|
+
* Get a single content entry by field value
|
|
62
|
+
* Similar to Contentful's getEntries with field filters
|
|
63
|
+
* @example
|
|
64
|
+
* // Get service by href field
|
|
65
|
+
* const service = await sdk.getContentByField('service-type-id', 'href', '/services/web-dev');
|
|
66
|
+
*
|
|
67
|
+
* // Get blog post by slug field
|
|
68
|
+
* const post = await sdk.getContentByField('blog-type-id', 'slug', 'my-first-post');
|
|
69
|
+
*/
|
|
70
|
+
getContentByField(contentTypeId: string, fieldName: string, fieldValue: string): Promise<ContentEntry | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Execute a GraphQL query against your project
|
|
73
|
+
*
|
|
74
|
+
* Provides flexible, powerful querying capabilities for your content.
|
|
75
|
+
* GraphQL allows you to request exactly the data you need in a single request.
|
|
76
|
+
*
|
|
77
|
+
* @param projectId - Your project ID
|
|
78
|
+
* @param query - GraphQL query string
|
|
79
|
+
* @param variables - Optional query variables for parameterized queries
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* // Get published blog posts
|
|
83
|
+
* const result = await sdk.graphql('project-id', `
|
|
84
|
+
* query {
|
|
85
|
+
* blogPostCollection(limit: 10, where: { status: "published" }) {
|
|
86
|
+
* items {
|
|
87
|
+
* id
|
|
88
|
+
* slug
|
|
89
|
+
* data
|
|
90
|
+
* }
|
|
91
|
+
* total
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
* `);
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Get a single entry by ID
|
|
98
|
+
* const result = await sdk.graphql('project-id', `
|
|
99
|
+
* query {
|
|
100
|
+
* blogPost(id: "entry-123") {
|
|
101
|
+
* id
|
|
102
|
+
* slug
|
|
103
|
+
* status
|
|
104
|
+
* data
|
|
105
|
+
* published_at
|
|
106
|
+
* }
|
|
107
|
+
* }
|
|
108
|
+
* `);
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* // Using variables for safer queries
|
|
112
|
+
* const result = await sdk.graphql('project-id', `
|
|
113
|
+
* query GetPost($slug: String!) {
|
|
114
|
+
* blogPostCollection(where: { slug: $slug }) {
|
|
115
|
+
* items { id data }
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* `, { slug: 'my-post' });
|
|
119
|
+
*/
|
|
120
|
+
graphql<T = any>(projectId: string, query: string, variables?: Record<string, any>): Promise<T>;
|
|
34
121
|
}
|
package/dist/client.js
CHANGED
|
@@ -13,10 +13,12 @@ export class MybeSDK {
|
|
|
13
13
|
else {
|
|
14
14
|
const isDevelopment = typeof process !== 'undefined' &&
|
|
15
15
|
process.env.NODE_ENV === 'development';
|
|
16
|
-
// Use
|
|
16
|
+
// Use custom domain for SDK endpoints
|
|
17
|
+
// TODO: Update production URL when production custom domain is ready
|
|
18
|
+
// Production: https://fizsdck7l0.execute-api.us-east-1.amazonaws.com/dev/api/v1
|
|
17
19
|
this.baseUrl = isDevelopment
|
|
18
|
-
? '
|
|
19
|
-
: 'https://
|
|
20
|
+
? 'https://sdk-dev.contensa.ai/dev/api/v1'
|
|
21
|
+
: 'https://sdk-dev.contensa.ai/dev/api/v1'; // Using dev URL for both temporarily
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
@@ -69,6 +71,65 @@ export class MybeSDK {
|
|
|
69
71
|
throw new MybeSDKError('An unknown error occurred');
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert kebab-case to camelCase for GraphQL field names
|
|
76
|
+
* Examples:
|
|
77
|
+
* "blog-post" → "blogPost"
|
|
78
|
+
* "case-study" → "caseStudy"
|
|
79
|
+
* "product" → "product" (unchanged)
|
|
80
|
+
*/
|
|
81
|
+
toCamelCase(str) {
|
|
82
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Automatically convert kebab-case content type slugs to camelCase in GraphQL queries
|
|
86
|
+
* This allows users to use their actual content type slugs (e.g., "blog-post")
|
|
87
|
+
* without needing to know about GraphQL's camelCase requirement
|
|
88
|
+
*
|
|
89
|
+
* Converts patterns like:
|
|
90
|
+
* "blog-post" → "blogPost"
|
|
91
|
+
* "blog-postCollection" → "blogPostCollection"
|
|
92
|
+
* "case-study(" → "caseStudy("
|
|
93
|
+
*
|
|
94
|
+
* @returns Object with converted query and mapping of original to converted field names
|
|
95
|
+
*/
|
|
96
|
+
convertQuerySlugs(query) {
|
|
97
|
+
const fieldMap = new Map();
|
|
98
|
+
// Match kebab-case slugs followed by Collection, (, {, or whitespace
|
|
99
|
+
const convertedQuery = query.replace(/([a-z]+(?:-[a-z]+)+)(Collection|\(|{|\s)/gi, (match, slug, suffix) => {
|
|
100
|
+
const camelCaseSlug = this.toCamelCase(slug);
|
|
101
|
+
const originalField = slug + suffix.replace(/[\(\{\s]/, ''); // Extract just 'Collection' if present
|
|
102
|
+
const convertedField = camelCaseSlug + suffix.replace(/[\(\{\s]/, '');
|
|
103
|
+
// Store mapping if it's a field name (with Collection or standalone)
|
|
104
|
+
if (suffix === 'Collection' || suffix === '(' || suffix === ' ' || suffix === '{') {
|
|
105
|
+
// Map both with and without Collection
|
|
106
|
+
fieldMap.set(camelCaseSlug, slug);
|
|
107
|
+
fieldMap.set(camelCaseSlug + 'Collection', slug + 'Collection');
|
|
108
|
+
fieldMap.set(camelCaseSlug + 'Collection', slug + 'Collection');
|
|
109
|
+
}
|
|
110
|
+
return camelCaseSlug + suffix;
|
|
111
|
+
});
|
|
112
|
+
return { query: convertedQuery, fieldMap };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Convert response keys back to the original format used in the query
|
|
116
|
+
* This allows users to access response fields using the same kebab-case format they used in the query
|
|
117
|
+
*/
|
|
118
|
+
convertResponseKeys(data, fieldMap) {
|
|
119
|
+
if (!data || typeof data !== 'object') {
|
|
120
|
+
return data;
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(data)) {
|
|
123
|
+
return data.map(item => this.convertResponseKeys(item, fieldMap));
|
|
124
|
+
}
|
|
125
|
+
const converted = {};
|
|
126
|
+
for (const [key, value] of Object.entries(data)) {
|
|
127
|
+
// Check if this key should be converted back
|
|
128
|
+
const originalKey = fieldMap.get(key) || key;
|
|
129
|
+
converted[originalKey] = this.convertResponseKeys(value, fieldMap);
|
|
130
|
+
}
|
|
131
|
+
return converted;
|
|
132
|
+
}
|
|
72
133
|
/**
|
|
73
134
|
* Build query string from options
|
|
74
135
|
*/
|
|
@@ -126,4 +187,109 @@ export class MybeSDK {
|
|
|
126
187
|
const queryString = this.buildQueryString(options);
|
|
127
188
|
return await this.request(`/content-entry/project/${projectId}${queryString}`);
|
|
128
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Get a single content entry by field value
|
|
192
|
+
* Similar to Contentful's getEntries with field filters
|
|
193
|
+
* @example
|
|
194
|
+
* // Get service by href field
|
|
195
|
+
* const service = await sdk.getContentByField('service-type-id', 'href', '/services/web-dev');
|
|
196
|
+
*
|
|
197
|
+
* // Get blog post by slug field
|
|
198
|
+
* const post = await sdk.getContentByField('blog-type-id', 'slug', 'my-first-post');
|
|
199
|
+
*/
|
|
200
|
+
async getContentByField(contentTypeId, fieldName, fieldValue) {
|
|
201
|
+
try {
|
|
202
|
+
const encodedValue = encodeURIComponent(fieldValue);
|
|
203
|
+
const response = await this.request(`/type/${contentTypeId}/field/${fieldName}/${encodedValue}`);
|
|
204
|
+
return response.data;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
// If it's a 404, return null instead of throwing
|
|
208
|
+
if (error instanceof NotFoundError) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Execute a GraphQL query against your project
|
|
216
|
+
*
|
|
217
|
+
* Provides flexible, powerful querying capabilities for your content.
|
|
218
|
+
* GraphQL allows you to request exactly the data you need in a single request.
|
|
219
|
+
*
|
|
220
|
+
* @param projectId - Your project ID
|
|
221
|
+
* @param query - GraphQL query string
|
|
222
|
+
* @param variables - Optional query variables for parameterized queries
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* // Get published blog posts
|
|
226
|
+
* const result = await sdk.graphql('project-id', `
|
|
227
|
+
* query {
|
|
228
|
+
* blogPostCollection(limit: 10, where: { status: "published" }) {
|
|
229
|
+
* items {
|
|
230
|
+
* id
|
|
231
|
+
* slug
|
|
232
|
+
* data
|
|
233
|
+
* }
|
|
234
|
+
* total
|
|
235
|
+
* }
|
|
236
|
+
* }
|
|
237
|
+
* `);
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* // Get a single entry by ID
|
|
241
|
+
* const result = await sdk.graphql('project-id', `
|
|
242
|
+
* query {
|
|
243
|
+
* blogPost(id: "entry-123") {
|
|
244
|
+
* id
|
|
245
|
+
* slug
|
|
246
|
+
* status
|
|
247
|
+
* data
|
|
248
|
+
* published_at
|
|
249
|
+
* }
|
|
250
|
+
* }
|
|
251
|
+
* `);
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* // Using variables for safer queries
|
|
255
|
+
* const result = await sdk.graphql('project-id', `
|
|
256
|
+
* query GetPost($slug: String!) {
|
|
257
|
+
* blogPostCollection(where: { slug: $slug }) {
|
|
258
|
+
* items { id data }
|
|
259
|
+
* }
|
|
260
|
+
* }
|
|
261
|
+
* `, { slug: 'my-post' });
|
|
262
|
+
*/
|
|
263
|
+
async graphql(projectId, query, variables) {
|
|
264
|
+
try {
|
|
265
|
+
// Automatically convert kebab-case slugs to camelCase for GraphQL
|
|
266
|
+
const { query: processedQuery, fieldMap } = this.convertQuerySlugs(query);
|
|
267
|
+
const response = await this.request('/graphql', {
|
|
268
|
+
method: 'POST',
|
|
269
|
+
headers: {
|
|
270
|
+
'x-project-id': projectId,
|
|
271
|
+
},
|
|
272
|
+
body: JSON.stringify({ query: processedQuery, variables }),
|
|
273
|
+
});
|
|
274
|
+
// Fixed: Safe check for errors array
|
|
275
|
+
if (response.errors && Array.isArray(response.errors) && response.errors.length > 0) {
|
|
276
|
+
const errorMessages = response.errors.map((e) => e.message).join(', ');
|
|
277
|
+
throw new MybeSDKError(`GraphQL query failed: ${errorMessages}`, 400, response.errors);
|
|
278
|
+
}
|
|
279
|
+
// Convert response keys back to original format (kebab-case if that's what user used)
|
|
280
|
+
const convertedData = this.convertResponseKeys(response.data, fieldMap);
|
|
281
|
+
return convertedData;
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
// Re-throw SDK errors
|
|
285
|
+
if (error instanceof MybeSDKError) {
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
// Handle other errors
|
|
289
|
+
if (error instanceof Error) {
|
|
290
|
+
throw new MybeSDKError(`GraphQL request failed: ${error.message}`, undefined, error);
|
|
291
|
+
}
|
|
292
|
+
throw new MybeSDKError('GraphQL request failed with unknown error');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
129
295
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -17,6 +17,15 @@ export interface ContentType {
|
|
|
17
17
|
created_at: string;
|
|
18
18
|
updated_at: string;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Content Type Metadata (for GraphQL responses)
|
|
22
|
+
*/
|
|
23
|
+
export interface ContentTypeMetadata {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
slug: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
20
29
|
/**
|
|
21
30
|
* Content Entry
|
|
22
31
|
*/
|
|
@@ -26,6 +35,7 @@ export interface ContentEntry {
|
|
|
26
35
|
project_id: string;
|
|
27
36
|
slug?: string;
|
|
28
37
|
status: 'draft' | 'published' | 'archived';
|
|
38
|
+
contentType?: ContentTypeMetadata;
|
|
29
39
|
data: Record<string, any>;
|
|
30
40
|
locale?: string;
|
|
31
41
|
created_by: string;
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mybe/sdk",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"dist/",
|
|
9
|
-
"README.md"
|
|
10
|
-
],
|
|
11
|
-
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"default": "./dist/index.js"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "tsc",
|
|
19
|
-
"dev": "tsc --watch",
|
|
20
|
-
"typecheck": "tsc --noEmit",
|
|
21
|
-
"test": "npm run build && npx tsx test.ts",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@mybe/sdk",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "tsc --watch",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "npm run build && npx tsx test.ts",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
}
|
|
24
|
+
}
|