@urbackend/sdk 0.2.0 → 0.2.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 +67 -44
- package/dist/index.cjs +560 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +343 -0
- package/dist/index.d.ts +343 -0
- package/dist/index.mjs +521 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +4 -1
- package/.prettierrc +0 -7
- package/eslint.config.js +0 -15
- package/src/client.ts +0 -143
- package/src/errors.ts +0 -90
- package/src/index.ts +0 -18
- package/src/modules/auth.ts +0 -209
- package/src/modules/database.ts +0 -124
- package/src/modules/mail.ts +0 -16
- package/src/modules/schema.ts +0 -24
- package/src/modules/storage.ts +0 -41
- package/src/types/index.ts +0 -154
- package/tests/auth.test.ts +0 -169
- package/tests/database.test.ts +0 -134
- package/tests/mail.test.ts +0 -41
- package/tests/schema.test.ts +0 -32
- package/tests/storage.test.ts +0 -62
- package/tests/tsconfig.json +0 -9
- package/tsconfig.json +0 -18
- package/tsup.config.ts +0 -16
- package/vitest.config.ts +0 -12
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# urbackend-sdk
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for [urBackend](https://urbackend.bitbros.in) — the instant Backend-as-a-Service for
|
|
3
|
+
Official TypeScript SDK for [urBackend](https://urbackend.bitbros.in) — the instant Backend-as-a-Service for MongoDB.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
|
+
```bash
|
|
6
7
|
npm install @urbackend/sdk
|
|
8
|
+
```
|
|
7
9
|
|
|
8
10
|
## Quick Start
|
|
9
11
|
```javascript
|
|
@@ -12,49 +14,80 @@ import urBackend from '@urbackend/sdk';
|
|
|
12
14
|
const client = urBackend({ apiKey: 'YOUR_API_KEY' });
|
|
13
15
|
|
|
14
16
|
// Auth
|
|
15
|
-
await client.auth.
|
|
16
|
-
await client.auth.
|
|
17
|
-
await client.auth.me();
|
|
17
|
+
const { accessToken } = await client.auth.login({ email, password });
|
|
18
|
+
const user = await client.auth.me();
|
|
18
19
|
|
|
19
|
-
// Database — collections are
|
|
20
|
-
await client.db.insert('
|
|
21
|
-
await client.db.getAll('
|
|
22
|
-
await client.db.getOne('products', id);
|
|
23
|
-
await client.db.update('products', id, { price: 79 });
|
|
24
|
-
await client.db.delete('products', id);
|
|
20
|
+
// Database — collections are managed via the urBackend Dashboard
|
|
21
|
+
await client.db.insert('posts', { title: 'Hello World' }, client.auth.getToken());
|
|
22
|
+
await client.db.getAll('posts', { sort: 'createdAt:desc', limit: 10 });
|
|
25
23
|
|
|
26
24
|
// Storage
|
|
27
|
-
await client.storage.upload(file);
|
|
28
|
-
|
|
25
|
+
const { url } = await client.storage.upload(file);
|
|
26
|
+
|
|
27
|
+
// Mail (Requires Secret Key)
|
|
28
|
+
await client.mail.send({
|
|
29
|
+
to: 'user@example.com',
|
|
30
|
+
subject: 'Welcome!',
|
|
31
|
+
text: 'Glad to have you.'
|
|
32
|
+
});
|
|
29
33
|
```
|
|
30
34
|
|
|
31
35
|
## API Reference
|
|
32
36
|
|
|
33
|
-
### Client
|
|
37
|
+
### Client Initialization
|
|
34
38
|
`urBackend({ apiKey: string, baseUrl?: string })`
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
### Auth (`client.auth`)
|
|
37
43
|
| Method | Params | Returns |
|
|
38
44
|
|--------|--------|---------|
|
|
39
|
-
| signUp | { email, password, name
|
|
40
|
-
| login
|
|
41
|
-
| me
|
|
42
|
-
| logout |
|
|
45
|
+
| `signUp` | `{ email, password, username?, name?, ... }` | `AuthUser` |
|
|
46
|
+
| `login` | `{ email, password }` | `AuthResponse` |
|
|
47
|
+
| `me` | `token?` | `AuthUser` |
|
|
48
|
+
| `logout` | `token?` | `{ success: boolean }` |
|
|
49
|
+
| `refreshToken` | `refreshToken?` | `AuthResponse` |
|
|
50
|
+
| `updateProfile`| `payload, token?` | `{ message: string }` |
|
|
51
|
+
| `changePassword`| `payload, token?` | `{ message: string }` |
|
|
52
|
+
| `verifyEmail` | `{ email, otp }` | `{ message: string }` |
|
|
53
|
+
| `publicProfile`| `username` | `AuthUser` |
|
|
54
|
+
| `socialStart` | `provider ('github'\|'google')` | `string` (Redirect URL) |
|
|
55
|
+
| `socialExchange`| `{ token, rtCode }` | `SocialExchangeResponse` |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Database (`client.db`)
|
|
60
|
+
Support for **Row-Level Security (RLS)** is built-in. Pass the user's `accessToken` as the final parameter to write routes if RLS is enabled for the collection.
|
|
43
61
|
|
|
44
|
-
### Database
|
|
45
62
|
| Method | Params | Returns |
|
|
46
63
|
|--------|--------|---------|
|
|
47
|
-
| getAll<T
|
|
48
|
-
| getOne<T
|
|
49
|
-
| insert<T
|
|
50
|
-
| update<T
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
| `getAll<T>` | `collection, params?` | `T[]` |
|
|
65
|
+
| `getOne<T>` | `collection, id, options?` | `T` |
|
|
66
|
+
| `insert<T>` | `collection, data, token?` | `T` |
|
|
67
|
+
| `update<T>` | `collection, id, data, token?` | `T` (Full Replace) |
|
|
68
|
+
| `patch<T>` | `collection, id, data, token?` | `T` (Partial Update) |
|
|
69
|
+
| `delete` | `collection, id, token?` | `{ deleted: boolean }` |
|
|
70
|
+
|
|
71
|
+
**Query Parameters (`params`):**
|
|
72
|
+
- `filter`: `{ price_gt: 100 }`
|
|
73
|
+
- `sort`: `"createdAt:desc"`
|
|
74
|
+
- `limit`: `50`
|
|
75
|
+
- `page`: `1`
|
|
76
|
+
- `populate`: `"author"` (Expand Reference fields)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Storage (`client.storage`)
|
|
54
81
|
| Method | Params | Returns |
|
|
55
82
|
|--------|--------|---------|
|
|
56
|
-
| upload | file, filename
|
|
57
|
-
| deleteFile | path | { deleted: boolean } |
|
|
83
|
+
| `upload` | `file (Buffer\|Blob), filename?` | `{ url, path, provider }` |
|
|
84
|
+
| `deleteFile` | `path` | `{ deleted: boolean }` |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### New Modules
|
|
89
|
+
- **`client.schema`**: Use `getSchema(collection)` to fetch visual field definitions.
|
|
90
|
+
- **`client.mail`**: Use `send(payload)` to send emails via Resend (Requires Secret Key).
|
|
58
91
|
|
|
59
92
|
## Error Handling
|
|
60
93
|
```javascript
|
|
@@ -63,23 +96,13 @@ import { AuthError, NotFoundError, RateLimitError } from '@urbackend/sdk';
|
|
|
63
96
|
try {
|
|
64
97
|
await client.db.getOne('products', id);
|
|
65
98
|
} catch (e) {
|
|
66
|
-
if (e instanceof
|
|
67
|
-
if (e instanceof
|
|
99
|
+
if (e instanceof AuthError) console.error('Invalid token or insufficient RLS permissions');
|
|
100
|
+
if (e instanceof NotFoundError) console.error('Resource not found');
|
|
101
|
+
if (e instanceof RateLimitError) console.error('Too many requests');
|
|
68
102
|
}
|
|
69
103
|
```
|
|
70
104
|
|
|
71
|
-
## TypeScript Support
|
|
72
|
-
```typescript
|
|
73
|
-
interface Product { _id: string; name: string; price: number; }
|
|
74
|
-
const products = await client.db.getAll<Product>('products');
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Limits
|
|
78
|
-
- Rate limit: 100 requests / 15 mins per IP
|
|
79
|
-
- Database: 50 MB per project
|
|
80
|
-
- Storage: 100 MB per project
|
|
81
|
-
- File upload: 5 MB per file
|
|
82
|
-
|
|
83
105
|
## Security
|
|
84
|
-
|
|
85
|
-
Use
|
|
106
|
+
- **Never expose your Secret Key (`sk_live_...`)** in frontend/browser code.
|
|
107
|
+
- Use **Publishable Keys (`pk_live_...`)** for client-side applications.
|
|
108
|
+
- Enable **RLS** in the urBackend Dashboard to allow secure writes from the frontend using the user's access token.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AuthError: () => AuthError,
|
|
24
|
+
AuthModule: () => AuthModule,
|
|
25
|
+
DatabaseModule: () => DatabaseModule,
|
|
26
|
+
MailModule: () => MailModule,
|
|
27
|
+
NotFoundError: () => NotFoundError,
|
|
28
|
+
RateLimitError: () => RateLimitError,
|
|
29
|
+
SchemaModule: () => SchemaModule,
|
|
30
|
+
StorageError: () => StorageError,
|
|
31
|
+
StorageModule: () => StorageModule,
|
|
32
|
+
UrBackendClient: () => UrBackendClient,
|
|
33
|
+
UrBackendError: () => UrBackendError,
|
|
34
|
+
ValidationError: () => ValidationError,
|
|
35
|
+
default: () => urBackend,
|
|
36
|
+
parseApiError: () => parseApiError
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// src/errors.ts
|
|
41
|
+
var UrBackendError = class extends Error {
|
|
42
|
+
constructor(message, statusCode, endpoint) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.message = message;
|
|
45
|
+
this.statusCode = statusCode;
|
|
46
|
+
this.endpoint = endpoint;
|
|
47
|
+
this.name = "UrBackendError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var AuthError = class extends UrBackendError {
|
|
51
|
+
constructor(message, statusCode, endpoint) {
|
|
52
|
+
super(message, statusCode, endpoint);
|
|
53
|
+
this.name = "AuthError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var NotFoundError = class extends UrBackendError {
|
|
57
|
+
constructor(message, endpoint) {
|
|
58
|
+
super(message, 404, endpoint);
|
|
59
|
+
this.name = "NotFoundError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var RateLimitError = class extends UrBackendError {
|
|
63
|
+
constructor(message, endpoint, retryAfter) {
|
|
64
|
+
super(message, 429, endpoint);
|
|
65
|
+
this.name = "RateLimitError";
|
|
66
|
+
this.retryAfter = retryAfter;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var StorageError = class extends UrBackendError {
|
|
70
|
+
constructor(message, statusCode, endpoint) {
|
|
71
|
+
super(message, statusCode, endpoint);
|
|
72
|
+
this.name = "StorageError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var ValidationError = class extends UrBackendError {
|
|
76
|
+
constructor(message, endpoint) {
|
|
77
|
+
super(message, 400, endpoint);
|
|
78
|
+
this.name = "ValidationError";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
async function parseApiError(response) {
|
|
82
|
+
const endpoint = new URL(response.url).pathname;
|
|
83
|
+
let message = "An unexpected error occurred";
|
|
84
|
+
let data;
|
|
85
|
+
try {
|
|
86
|
+
data = await response.json();
|
|
87
|
+
if (typeof data === "object" && data !== null && "message" in data) {
|
|
88
|
+
message = data.message || message;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
message = response.statusText || message;
|
|
92
|
+
}
|
|
93
|
+
const status = response.status;
|
|
94
|
+
if (status === 401 || status === 403) {
|
|
95
|
+
return new AuthError(message, status, endpoint);
|
|
96
|
+
}
|
|
97
|
+
if (status === 404) {
|
|
98
|
+
return new NotFoundError(message, endpoint);
|
|
99
|
+
}
|
|
100
|
+
if (status === 429) {
|
|
101
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
102
|
+
return new RateLimitError(message, endpoint, retryAfter ? parseInt(retryAfter, 10) : void 0);
|
|
103
|
+
}
|
|
104
|
+
if (status === 400) {
|
|
105
|
+
return new ValidationError(message, endpoint);
|
|
106
|
+
}
|
|
107
|
+
if (endpoint.includes("/api/storage")) {
|
|
108
|
+
return new StorageError(message, status, endpoint);
|
|
109
|
+
}
|
|
110
|
+
return new UrBackendError(message, status, endpoint);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/modules/auth.ts
|
|
114
|
+
var AuthModule = class {
|
|
115
|
+
constructor(client) {
|
|
116
|
+
this.client = client;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create a new user account
|
|
120
|
+
*/
|
|
121
|
+
async signUp(payload) {
|
|
122
|
+
return this.client.request("POST", "/api/userAuth/signup", { body: payload });
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Log in an existing user and store the session token
|
|
126
|
+
*/
|
|
127
|
+
async login(payload) {
|
|
128
|
+
const response = await this.client.request("POST", "/api/userAuth/login", {
|
|
129
|
+
body: payload
|
|
130
|
+
});
|
|
131
|
+
this.sessionToken = response.accessToken || response.token;
|
|
132
|
+
if (!response.accessToken && response.token) {
|
|
133
|
+
console.warn(
|
|
134
|
+
'urbackend-sdk: The server returned "token" which is deprecated. Please update your backend to return "accessToken".'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return response;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the current authenticated user's profile
|
|
141
|
+
*/
|
|
142
|
+
async me(token) {
|
|
143
|
+
const activeToken = token || this.sessionToken;
|
|
144
|
+
if (!activeToken) {
|
|
145
|
+
throw new AuthError(
|
|
146
|
+
"Authentication token is required for /me endpoint",
|
|
147
|
+
401,
|
|
148
|
+
"/api/userAuth/me"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return this.client.request("GET", "/api/userAuth/me", { token: activeToken });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Update the current authenticated user's profile
|
|
155
|
+
*/
|
|
156
|
+
async updateProfile(payload, token) {
|
|
157
|
+
const activeToken = token || this.sessionToken;
|
|
158
|
+
if (!activeToken) {
|
|
159
|
+
throw new AuthError("Authentication token is required to update profile", 401, "/api/userAuth/update-profile");
|
|
160
|
+
}
|
|
161
|
+
return this.client.request("PUT", "/api/userAuth/update-profile", {
|
|
162
|
+
body: payload,
|
|
163
|
+
token: activeToken
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Change the current authenticated user's password
|
|
168
|
+
*/
|
|
169
|
+
async changePassword(payload, token) {
|
|
170
|
+
const activeToken = token || this.sessionToken;
|
|
171
|
+
if (!activeToken) {
|
|
172
|
+
throw new AuthError("Authentication token is required to change password", 401, "/api/userAuth/change-password");
|
|
173
|
+
}
|
|
174
|
+
return this.client.request("PUT", "/api/userAuth/change-password", {
|
|
175
|
+
body: payload,
|
|
176
|
+
token: activeToken
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Verify user email with OTP
|
|
181
|
+
*/
|
|
182
|
+
async verifyEmail(payload) {
|
|
183
|
+
return this.client.request("POST", "/api/userAuth/verify-email", {
|
|
184
|
+
body: payload
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Resend verification OTP
|
|
189
|
+
*/
|
|
190
|
+
async resendVerificationOtp(payload) {
|
|
191
|
+
return this.client.request("POST", "/api/userAuth/resend-verification-otp", {
|
|
192
|
+
body: payload
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Request password reset OTP
|
|
197
|
+
*/
|
|
198
|
+
async requestPasswordReset(payload) {
|
|
199
|
+
return this.client.request("POST", "/api/userAuth/request-password-reset", {
|
|
200
|
+
body: payload
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Reset user password with OTP
|
|
205
|
+
*/
|
|
206
|
+
async resetPassword(payload) {
|
|
207
|
+
return this.client.request("POST", "/api/userAuth/reset-password", {
|
|
208
|
+
body: payload
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get public-safe profile by username
|
|
213
|
+
*/
|
|
214
|
+
async publicProfile(username) {
|
|
215
|
+
return this.client.request("GET", `/api/userAuth/public/${username}`);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Refresh the access token
|
|
219
|
+
* @param refreshToken Optional refresh token for header mode. If omitted, uses cookie mode.
|
|
220
|
+
*/
|
|
221
|
+
async refreshToken(refreshToken) {
|
|
222
|
+
const options = {};
|
|
223
|
+
if (refreshToken) {
|
|
224
|
+
options.headers = { "x-refresh-token": refreshToken, "x-refresh-token-mode": "header" };
|
|
225
|
+
} else {
|
|
226
|
+
options.credentials = "include";
|
|
227
|
+
}
|
|
228
|
+
const response = await this.client.request("POST", "/api/userAuth/refresh-token", options);
|
|
229
|
+
this.sessionToken = response.accessToken || response.token;
|
|
230
|
+
return response;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Returns the start URL for social authentication.
|
|
234
|
+
* Redirect the user's browser to this URL to begin the flow.
|
|
235
|
+
*/
|
|
236
|
+
socialStart(provider) {
|
|
237
|
+
return `${this.client.getBaseUrl()}/api/userAuth/social/${provider}/start?key=${this.client.getApiKey()}`;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Exchange social auth rtCode for a refresh token
|
|
241
|
+
*/
|
|
242
|
+
async socialExchange(payload) {
|
|
243
|
+
return this.client.request("POST", "/api/userAuth/social/exchange", {
|
|
244
|
+
body: payload
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Revoke the current session and clear local state
|
|
249
|
+
*/
|
|
250
|
+
async logout(token) {
|
|
251
|
+
const activeToken = token || this.sessionToken;
|
|
252
|
+
let result = { success: true, message: "Logged out locally" };
|
|
253
|
+
if (activeToken) {
|
|
254
|
+
try {
|
|
255
|
+
result = await this.client.request(
|
|
256
|
+
"POST",
|
|
257
|
+
"/api/userAuth/logout",
|
|
258
|
+
{ token: activeToken, credentials: "include" }
|
|
259
|
+
);
|
|
260
|
+
} catch (e) {
|
|
261
|
+
console.warn("urbackend-sdk: Server logout failed", e);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
this.sessionToken = void 0;
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Manually set the session token (e.g. after social auth exchange)
|
|
269
|
+
*/
|
|
270
|
+
setToken(token) {
|
|
271
|
+
this.sessionToken = token;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get the current stored session token
|
|
275
|
+
*/
|
|
276
|
+
getToken() {
|
|
277
|
+
return this.sessionToken;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/modules/database.ts
|
|
282
|
+
var DatabaseModule = class {
|
|
283
|
+
constructor(client) {
|
|
284
|
+
this.client = client;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Fetch all documents from a collection with optional query parameters
|
|
288
|
+
*/
|
|
289
|
+
async getAll(collection, params = {}) {
|
|
290
|
+
const queryString = this.buildQueryString(params);
|
|
291
|
+
const path = `/api/data/${collection}${queryString}`;
|
|
292
|
+
try {
|
|
293
|
+
return await this.client.request("GET", path);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
if (e instanceof NotFoundError) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
throw e;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Fetch a single document by its ID
|
|
303
|
+
*/
|
|
304
|
+
async getOne(collection, id, options = {}) {
|
|
305
|
+
const queryString = this.buildQueryString(options);
|
|
306
|
+
return this.client.request("GET", `/api/data/${collection}/${id}${queryString}`);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Insert a new document into a collection
|
|
310
|
+
*/
|
|
311
|
+
async insert(collection, data, token) {
|
|
312
|
+
return this.client.request("POST", `/api/data/${collection}`, {
|
|
313
|
+
body: data,
|
|
314
|
+
token
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Update an existing document by its ID (Full replacement)
|
|
319
|
+
*/
|
|
320
|
+
async update(collection, id, data, token) {
|
|
321
|
+
return this.client.request("PUT", `/api/data/${collection}/${id}`, {
|
|
322
|
+
body: data,
|
|
323
|
+
token
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Partially update an existing document by its ID
|
|
328
|
+
*/
|
|
329
|
+
async patch(collection, id, data, token) {
|
|
330
|
+
return this.client.request("PATCH", `/api/data/${collection}/${id}`, {
|
|
331
|
+
body: data,
|
|
332
|
+
token
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Delete a document by its ID
|
|
337
|
+
*/
|
|
338
|
+
async delete(collection, id, token) {
|
|
339
|
+
const result = await this.client.request(
|
|
340
|
+
"DELETE",
|
|
341
|
+
`/api/data/${collection}/${id}`,
|
|
342
|
+
{ token }
|
|
343
|
+
);
|
|
344
|
+
const deleted = typeof result === "object" && result !== null && (result.id === id || result.message === "Document deleted");
|
|
345
|
+
return { deleted };
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Internal helper to build query string from QueryParams
|
|
349
|
+
*/
|
|
350
|
+
buildQueryString(params) {
|
|
351
|
+
const searchParams = new URLSearchParams();
|
|
352
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
353
|
+
if (value === void 0 || value === null) return;
|
|
354
|
+
if (key === "filter" && typeof value === "object") {
|
|
355
|
+
Object.entries(value).forEach(([fKey, fValue]) => {
|
|
356
|
+
if (fValue !== void 0 && fValue !== null) {
|
|
357
|
+
searchParams.append(fKey, String(fValue));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
} else if ((key === "populate" || key === "expand") && Array.isArray(value)) {
|
|
361
|
+
searchParams.append(key, value.join(","));
|
|
362
|
+
} else {
|
|
363
|
+
searchParams.append(key, String(value));
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
const str = searchParams.toString();
|
|
367
|
+
return str ? `?${str}` : "";
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/modules/storage.ts
|
|
372
|
+
var StorageModule = class {
|
|
373
|
+
constructor(client) {
|
|
374
|
+
this.client = client;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Upload a file to storage
|
|
378
|
+
*/
|
|
379
|
+
async upload(file, filename) {
|
|
380
|
+
const formData = new FormData();
|
|
381
|
+
if (typeof window === "undefined" && typeof Buffer !== "undefined" && Buffer.isBuffer(file)) {
|
|
382
|
+
const blob = new Blob([file]);
|
|
383
|
+
formData.append("file", blob, filename || "file");
|
|
384
|
+
} else {
|
|
385
|
+
formData.append("file", file, filename);
|
|
386
|
+
}
|
|
387
|
+
return this.client.request("POST", "/api/storage/upload", {
|
|
388
|
+
body: formData,
|
|
389
|
+
isMultipart: true
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Delete a file from storage by its path
|
|
394
|
+
*/
|
|
395
|
+
async deleteFile(path) {
|
|
396
|
+
return this.client.request("DELETE", "/api/storage/file", {
|
|
397
|
+
body: { path }
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/modules/schema.ts
|
|
403
|
+
var SchemaModule = class {
|
|
404
|
+
constructor(client) {
|
|
405
|
+
this.client = client;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Fetch the schema definition for a collection
|
|
409
|
+
*/
|
|
410
|
+
async getSchema(collection) {
|
|
411
|
+
const trimmedCollection = collection.trim();
|
|
412
|
+
if (trimmedCollection === "") {
|
|
413
|
+
throw new Error("Collection name cannot be empty or whitespace-only");
|
|
414
|
+
}
|
|
415
|
+
const encodedCollection = encodeURIComponent(trimmedCollection);
|
|
416
|
+
const response = await this.client.request("GET", `/api/schemas/${encodedCollection}`);
|
|
417
|
+
return response.collection;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/modules/mail.ts
|
|
422
|
+
var MailModule = class {
|
|
423
|
+
constructor(client) {
|
|
424
|
+
this.client = client;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Send an email using the urBackend mail service.
|
|
428
|
+
* Note: This requires a Secret Key (sk_live_...) and should be called from a server environment.
|
|
429
|
+
*/
|
|
430
|
+
async send(payload) {
|
|
431
|
+
return this.client.request("POST", "/api/mail/send", {
|
|
432
|
+
body: payload
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// src/client.ts
|
|
438
|
+
var UrBackendClient = class {
|
|
439
|
+
constructor(config) {
|
|
440
|
+
this.apiKey = config.apiKey;
|
|
441
|
+
this.baseUrl = config.baseUrl || "https://api.ub.bitbros.in";
|
|
442
|
+
this.headers = config.headers || {};
|
|
443
|
+
if (typeof window !== "undefined" && this.apiKey.startsWith("sk_live_")) {
|
|
444
|
+
console.warn(
|
|
445
|
+
"\u26A0\uFE0F urbackend-sdk: Avoid exposing your Secret Key (sk_live_...) in client-side code. This can lead to unauthorized access to your account and data. Use your Publishable Key (pk_live_...) instead."
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
get auth() {
|
|
450
|
+
if (!this._auth) {
|
|
451
|
+
this._auth = new AuthModule(this);
|
|
452
|
+
}
|
|
453
|
+
return this._auth;
|
|
454
|
+
}
|
|
455
|
+
get db() {
|
|
456
|
+
if (!this._db) {
|
|
457
|
+
this._db = new DatabaseModule(this);
|
|
458
|
+
}
|
|
459
|
+
return this._db;
|
|
460
|
+
}
|
|
461
|
+
get storage() {
|
|
462
|
+
if (!this._storage) {
|
|
463
|
+
this._storage = new StorageModule(this);
|
|
464
|
+
}
|
|
465
|
+
return this._storage;
|
|
466
|
+
}
|
|
467
|
+
get schema() {
|
|
468
|
+
if (!this._schema) {
|
|
469
|
+
this._schema = new SchemaModule(this);
|
|
470
|
+
}
|
|
471
|
+
return this._schema;
|
|
472
|
+
}
|
|
473
|
+
get mail() {
|
|
474
|
+
if (!this._mail) {
|
|
475
|
+
this._mail = new MailModule(this);
|
|
476
|
+
}
|
|
477
|
+
return this._mail;
|
|
478
|
+
}
|
|
479
|
+
getBaseUrl() {
|
|
480
|
+
return this.baseUrl;
|
|
481
|
+
}
|
|
482
|
+
getApiKey() {
|
|
483
|
+
return this.apiKey;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Internal request handler
|
|
487
|
+
*/
|
|
488
|
+
async request(method, path, options = {}) {
|
|
489
|
+
const url = `${this.baseUrl}${path}`;
|
|
490
|
+
const headers = {
|
|
491
|
+
"x-api-key": this.apiKey,
|
|
492
|
+
"User-Agent": `urbackend-sdk-js/0.2.0`,
|
|
493
|
+
...this.headers
|
|
494
|
+
};
|
|
495
|
+
if (options.token) {
|
|
496
|
+
headers["Authorization"] = `Bearer ${options.token}`;
|
|
497
|
+
}
|
|
498
|
+
if (options.headers) {
|
|
499
|
+
Object.assign(headers, options.headers);
|
|
500
|
+
}
|
|
501
|
+
let requestBody;
|
|
502
|
+
if (options.isMultipart) {
|
|
503
|
+
requestBody = options.body;
|
|
504
|
+
} else if (options.body) {
|
|
505
|
+
headers["Content-Type"] = "application/json";
|
|
506
|
+
requestBody = JSON.stringify(options.body);
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const response = await fetch(url, {
|
|
510
|
+
method,
|
|
511
|
+
headers,
|
|
512
|
+
body: requestBody,
|
|
513
|
+
credentials: options.credentials
|
|
514
|
+
});
|
|
515
|
+
if (!response.ok) {
|
|
516
|
+
throw await parseApiError(response);
|
|
517
|
+
}
|
|
518
|
+
const contentType = response.headers.get("content-type");
|
|
519
|
+
if (contentType && contentType.includes("application/json")) {
|
|
520
|
+
const json = await response.json();
|
|
521
|
+
if (json.data !== void 0) {
|
|
522
|
+
return json.data;
|
|
523
|
+
}
|
|
524
|
+
return json;
|
|
525
|
+
}
|
|
526
|
+
return await response.text();
|
|
527
|
+
} catch (error) {
|
|
528
|
+
if (error instanceof UrBackendError) {
|
|
529
|
+
throw error;
|
|
530
|
+
}
|
|
531
|
+
throw new UrBackendError(
|
|
532
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
533
|
+
0,
|
|
534
|
+
path
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// src/index.ts
|
|
541
|
+
function urBackend(config) {
|
|
542
|
+
return new UrBackendClient(config);
|
|
543
|
+
}
|
|
544
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
545
|
+
0 && (module.exports = {
|
|
546
|
+
AuthError,
|
|
547
|
+
AuthModule,
|
|
548
|
+
DatabaseModule,
|
|
549
|
+
MailModule,
|
|
550
|
+
NotFoundError,
|
|
551
|
+
RateLimitError,
|
|
552
|
+
SchemaModule,
|
|
553
|
+
StorageError,
|
|
554
|
+
StorageModule,
|
|
555
|
+
UrBackendClient,
|
|
556
|
+
UrBackendError,
|
|
557
|
+
ValidationError,
|
|
558
|
+
parseApiError
|
|
559
|
+
});
|
|
560
|
+
//# sourceMappingURL=index.cjs.map
|