@centrali-io/centrali-sdk 2.0.0 → 2.0.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/README.md +197 -0
- package/dist/index.js +29 -10
- package/index.ts +30 -11
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Centrali JavaScript/TypeScript SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for Centrali - Build modern web applications without managing infrastructure.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@centrali-io/centrali-sdk)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @centrali-io/centrali-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { CentraliSDK } from '@centrali-io/centrali-sdk';
|
|
17
|
+
|
|
18
|
+
// Initialize the SDK
|
|
19
|
+
const centrali = new CentraliSDK({
|
|
20
|
+
baseUrl: 'https://centrali.io',
|
|
21
|
+
workspaceId: 'your-workspace',
|
|
22
|
+
token: 'your-api-key'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create a record
|
|
26
|
+
const product = await centrali.createRecord('Product', {
|
|
27
|
+
name: 'Awesome Product',
|
|
28
|
+
price: 99.99,
|
|
29
|
+
inStock: true
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log('Created product:', product.data);
|
|
33
|
+
|
|
34
|
+
// Invoke a compute function
|
|
35
|
+
const result = await centrali.invokeFunction('calculate-discount', {
|
|
36
|
+
productId: product.id,
|
|
37
|
+
couponCode: 'SAVE20'
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log('Discount result:', result.data);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Authentication
|
|
44
|
+
|
|
45
|
+
The SDK supports two authentication methods:
|
|
46
|
+
|
|
47
|
+
### Bearer Token (User Authentication)
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const centrali = new CentraliSDK({
|
|
51
|
+
baseUrl: 'https://centrali.io',
|
|
52
|
+
workspaceId: 'your-workspace',
|
|
53
|
+
token: 'your-jwt-token'
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Service Account (Server-to-Server)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const centrali = new CentraliSDK({
|
|
61
|
+
baseUrl: 'https://centrali.io',
|
|
62
|
+
workspaceId: 'your-workspace',
|
|
63
|
+
clientId: process.env.CENTRALI_CLIENT_ID,
|
|
64
|
+
clientSecret: process.env.CENTRALI_CLIENT_SECRET
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// The SDK automatically fetches and manages tokens
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
- ✅ **Type-safe API** with full TypeScript support
|
|
73
|
+
- ✅ **Automatic authentication** and token management
|
|
74
|
+
- ✅ **Records management** - Create, read, update, delete records
|
|
75
|
+
- ✅ **Query operations** - Powerful data querying with filters and sorting
|
|
76
|
+
- ✅ **Compute functions** - Execute serverless functions
|
|
77
|
+
- ✅ **File uploads** - Upload files to Centrali storage
|
|
78
|
+
- ✅ **Service accounts** - Automatic token refresh for server-to-server auth
|
|
79
|
+
|
|
80
|
+
## Core Operations
|
|
81
|
+
|
|
82
|
+
### Records
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Create a record
|
|
86
|
+
const record = await centrali.createRecord('StructureName', {
|
|
87
|
+
field1: 'value1',
|
|
88
|
+
field2: 123
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Get a record
|
|
92
|
+
const record = await centrali.getRecord('StructureName', 'record-id');
|
|
93
|
+
|
|
94
|
+
// Update a record
|
|
95
|
+
await centrali.updateRecord('StructureName', 'record-id', {
|
|
96
|
+
field1: 'new value'
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Delete a record
|
|
100
|
+
await centrali.deleteRecord('StructureName', 'record-id');
|
|
101
|
+
|
|
102
|
+
// Query records
|
|
103
|
+
const products = await centrali.queryRecords('Product', {
|
|
104
|
+
filter: 'inStock = true AND price < 100',
|
|
105
|
+
sort: '-createdAt',
|
|
106
|
+
limit: 10
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Compute Functions
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Execute a function
|
|
114
|
+
const result = await centrali.invokeFunction('myFunction', {
|
|
115
|
+
data: 'your-input-data'
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### File Uploads
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Upload a file
|
|
123
|
+
const uploadResult = await centrali.uploadFile(
|
|
124
|
+
file,
|
|
125
|
+
'folder-name',
|
|
126
|
+
true // make publicly accessible
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
console.log('File URL:', uploadResult.data);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## TypeScript Support
|
|
133
|
+
|
|
134
|
+
The SDK includes full TypeScript definitions for type-safe development:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface Product {
|
|
138
|
+
name: string;
|
|
139
|
+
price: number;
|
|
140
|
+
inStock: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const product = await centrali.createRecord<Product>('Product', {
|
|
144
|
+
name: 'Laptop',
|
|
145
|
+
price: 999.99,
|
|
146
|
+
inStock: true
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// TypeScript knows product.data is of type Product
|
|
150
|
+
console.log(product.data.price);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Error Handling
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
try {
|
|
157
|
+
const record = await centrali.createRecord('Product', productData);
|
|
158
|
+
console.log('Success:', record);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error.response?.status === 400) {
|
|
161
|
+
console.error('Validation error:', error.response.data);
|
|
162
|
+
} else if (error.response?.status === 401) {
|
|
163
|
+
console.error('Authentication failed');
|
|
164
|
+
} else {
|
|
165
|
+
console.error('Error:', error.message);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Documentation
|
|
171
|
+
|
|
172
|
+
📚 **Full documentation available at**: [docs.centrali.io](https://docs.centrali.io)
|
|
173
|
+
|
|
174
|
+
- [Complete SDK Guide](https://docs.centrali.io/guides/centrali-sdk)
|
|
175
|
+
- [API Reference](https://docs.centrali.io/api-reference/overview)
|
|
176
|
+
- [Quick Start Tutorial](https://docs.centrali.io/getting-started/02-quickstart)
|
|
177
|
+
- [Compute Functions Guide](https://docs.centrali.io/guides/compute-functions-guide)
|
|
178
|
+
|
|
179
|
+
## Examples
|
|
180
|
+
|
|
181
|
+
Check out complete example applications:
|
|
182
|
+
- [Blog Platform](https://docs.centrali.io/examples/blog-platform)
|
|
183
|
+
- [E-commerce API](https://docs.centrali.io/examples/ecommerce-api)
|
|
184
|
+
|
|
185
|
+
## Support
|
|
186
|
+
|
|
187
|
+
- 📖 [Documentation](https://docs.centrali.io)
|
|
188
|
+
- 🐛 [GitHub Issues](https://github.com/blueinit/centrali-sdk/issues)
|
|
189
|
+
- 💬 [Community Support](https://centrali.io/community)
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
ISC © Blueinit
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
**Built with ❤️ by the Centrali team**
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.CentraliSDK = void 0;
|
|
22
|
+
exports.getApiUrl = getApiUrl;
|
|
22
23
|
exports.getAuthUrl = getAuthUrl;
|
|
23
24
|
exports.fetchClientToken = fetchClientToken;
|
|
24
25
|
exports.getRecordApiPath = getRecordApiPath;
|
|
@@ -31,25 +32,42 @@ function encodeFormData(data) {
|
|
|
31
32
|
return new URLSearchParams(data).toString();
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
|
-
* Generate the
|
|
35
|
+
* Generate the API URL from the base URL by adding the 'api.' subdomain.
|
|
36
|
+
* E.g., https://centrali.io -> https://api.centrali.io
|
|
35
37
|
*/
|
|
36
|
-
function
|
|
37
|
-
const url = new URL(
|
|
38
|
-
//
|
|
39
|
-
|
|
38
|
+
function getApiUrl(baseUrl) {
|
|
39
|
+
const url = new URL(baseUrl);
|
|
40
|
+
// If already has 'api.' subdomain, return as-is
|
|
41
|
+
if (url.hostname.startsWith('api.')) {
|
|
42
|
+
return `${url.protocol}//${url.hostname}`;
|
|
43
|
+
}
|
|
44
|
+
return `${url.protocol}//api.${url.hostname}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generate the auth server URL from the base URL.
|
|
48
|
+
* E.g., https://centrali.io -> https://auth.centrali.io
|
|
49
|
+
*/
|
|
50
|
+
function getAuthUrl(baseUrl) {
|
|
51
|
+
const url = new URL(baseUrl);
|
|
52
|
+
// Strip 'api.' prefix if present to get root domain
|
|
53
|
+
const hostname = url.hostname.startsWith('api.')
|
|
54
|
+
? url.hostname.slice(4)
|
|
55
|
+
: url.hostname;
|
|
56
|
+
return `${url.protocol}//auth.${hostname}`;
|
|
40
57
|
}
|
|
41
58
|
/**
|
|
42
59
|
* Retrieve an access token using the Client Credentials flow.
|
|
43
60
|
*/
|
|
44
|
-
function fetchClientToken(clientId, clientSecret,
|
|
61
|
+
function fetchClientToken(clientId, clientSecret, baseUrl) {
|
|
45
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
const authUrl = getAuthUrl(
|
|
63
|
+
const authUrl = getAuthUrl(baseUrl);
|
|
64
|
+
const apiUrl = getApiUrl(baseUrl);
|
|
47
65
|
const tokenEndpoint = `${authUrl}/token`;
|
|
48
66
|
const form = encodeFormData({
|
|
49
67
|
grant_type: 'client_credentials',
|
|
50
68
|
client_id: clientId,
|
|
51
69
|
client_secret: clientSecret,
|
|
52
|
-
resource: `${
|
|
70
|
+
resource: `${apiUrl}/data`
|
|
53
71
|
});
|
|
54
72
|
const resp = yield axios_1.default.post(tokenEndpoint, form, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
55
73
|
proxy: false
|
|
@@ -84,7 +102,8 @@ class CentraliSDK {
|
|
|
84
102
|
this.token = null;
|
|
85
103
|
this.options = options;
|
|
86
104
|
this.token = options.token || null;
|
|
87
|
-
|
|
105
|
+
const apiUrl = getApiUrl(options.baseUrl);
|
|
106
|
+
this.axios = axios_1.default.create(Object.assign({ baseURL: apiUrl, paramsSerializer: (params) => qs_1.default.stringify(params, { arrayFormat: "repeat" }), proxy: false }, options.axiosConfig));
|
|
88
107
|
// Attach async interceptor to fetch token on first request if needed
|
|
89
108
|
this.axios.interceptors.request.use((config) => __awaiter(this, void 0, void 0, function* () {
|
|
90
109
|
if (!this.token && this.options.clientId && this.options.clientSecret) {
|
|
@@ -195,7 +214,7 @@ exports.CentraliSDK = CentraliSDK;
|
|
|
195
214
|
* import { CentraliSDK, CentraliSDKOptions } from 'centrali-sdk';
|
|
196
215
|
*
|
|
197
216
|
* const options: CentraliSDKOptions = {
|
|
198
|
-
* baseUrl: 'https://
|
|
217
|
+
* baseUrl: 'https://centrali.io',
|
|
199
218
|
* clientId: process.env.CLIENT_ID,
|
|
200
219
|
* clientSecret: process.env.CLIENT_SECRET,
|
|
201
220
|
* };
|
package/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ function encodeFormData(data: Record<string, string>): string {
|
|
|
16
16
|
* Options for initializing the Centrali SDK client.
|
|
17
17
|
*/
|
|
18
18
|
export interface CentraliSDKOptions {
|
|
19
|
-
/** Base URL of
|
|
19
|
+
/** Base URL of Centrali (e.g. https://centrali.io). The SDK automatically uses api.centrali.io for API calls. */
|
|
20
20
|
baseUrl: string;
|
|
21
21
|
workspaceId: string;
|
|
22
22
|
/** Optional initial bearer token for authentication */
|
|
@@ -43,12 +43,29 @@ export interface ApiResponse<T> {
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* Generate the
|
|
46
|
+
* Generate the API URL from the base URL by adding the 'api.' subdomain.
|
|
47
|
+
* E.g., https://centrali.io -> https://api.centrali.io
|
|
47
48
|
*/
|
|
48
|
-
export function
|
|
49
|
-
const url = new URL(
|
|
50
|
-
//
|
|
51
|
-
|
|
49
|
+
export function getApiUrl(baseUrl: string): string {
|
|
50
|
+
const url = new URL(baseUrl);
|
|
51
|
+
// If already has 'api.' subdomain, return as-is
|
|
52
|
+
if (url.hostname.startsWith('api.')) {
|
|
53
|
+
return `${url.protocol}//${url.hostname}`;
|
|
54
|
+
}
|
|
55
|
+
return `${url.protocol}//api.${url.hostname}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate the auth server URL from the base URL.
|
|
60
|
+
* E.g., https://centrali.io -> https://auth.centrali.io
|
|
61
|
+
*/
|
|
62
|
+
export function getAuthUrl(baseUrl: string): string {
|
|
63
|
+
const url = new URL(baseUrl);
|
|
64
|
+
// Strip 'api.' prefix if present to get root domain
|
|
65
|
+
const hostname = url.hostname.startsWith('api.')
|
|
66
|
+
? url.hostname.slice(4)
|
|
67
|
+
: url.hostname;
|
|
68
|
+
return `${url.protocol}//auth.${hostname}`;
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
/**
|
|
@@ -57,15 +74,16 @@ export function getAuthUrl(apiBaseUrl: string): string {
|
|
|
57
74
|
export async function fetchClientToken(
|
|
58
75
|
clientId: string,
|
|
59
76
|
clientSecret: string,
|
|
60
|
-
|
|
77
|
+
baseUrl: string
|
|
61
78
|
): Promise<string> {
|
|
62
|
-
const authUrl = getAuthUrl(
|
|
79
|
+
const authUrl = getAuthUrl(baseUrl);
|
|
80
|
+
const apiUrl = getApiUrl(baseUrl);
|
|
63
81
|
const tokenEndpoint = `${authUrl}/token`;
|
|
64
82
|
const form = encodeFormData({
|
|
65
83
|
grant_type: 'client_credentials',
|
|
66
84
|
client_id: clientId,
|
|
67
85
|
client_secret: clientSecret,
|
|
68
|
-
resource: `${
|
|
86
|
+
resource: `${apiUrl}/data`
|
|
69
87
|
});
|
|
70
88
|
const resp = await axios.post(
|
|
71
89
|
tokenEndpoint,
|
|
@@ -113,8 +131,9 @@ export class CentraliSDK {
|
|
|
113
131
|
constructor(options: CentraliSDKOptions) {
|
|
114
132
|
this.options = options;
|
|
115
133
|
this.token = options.token || null;
|
|
134
|
+
const apiUrl = getApiUrl(options.baseUrl);
|
|
116
135
|
this.axios = axios.create({
|
|
117
|
-
baseURL:
|
|
136
|
+
baseURL: apiUrl,
|
|
118
137
|
paramsSerializer: (params: Record<string, any>): string =>
|
|
119
138
|
qs.stringify(params, { arrayFormat: "repeat" }),
|
|
120
139
|
proxy: false,
|
|
@@ -299,7 +318,7 @@ export class CentraliSDK {
|
|
|
299
318
|
* import { CentraliSDK, CentraliSDKOptions } from 'centrali-sdk';
|
|
300
319
|
*
|
|
301
320
|
* const options: CentraliSDKOptions = {
|
|
302
|
-
* baseUrl: 'https://
|
|
321
|
+
* baseUrl: 'https://centrali.io',
|
|
303
322
|
* clientId: process.env.CLIENT_ID,
|
|
304
323
|
* clientSecret: process.env.CLIENT_SECRET,
|
|
305
324
|
* };
|