@snackbase/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2635 -0
- package/dist/{index-Dr6K4PMl.d.mts → index.d.cts} +199 -28
- package/dist/index.d.mts +2289 -2
- package/dist/index.mjs +135 -21
- package/package.json +30 -38
- package/CHANGELOG.md +0 -61
- package/README.md +0 -287
- package/dist/react/index.d.mts +0 -63
- package/dist/react/index.mjs +0 -271
package/dist/index.mjs
CHANGED
|
@@ -319,9 +319,9 @@ var Logger = class {
|
|
|
319
319
|
constructor(level = LogLevel.NONE) {
|
|
320
320
|
this.level = level;
|
|
321
321
|
this.handlers.push((entry) => {
|
|
322
|
-
const { level
|
|
322
|
+
const { level, message, data } = entry;
|
|
323
323
|
const args = data ? [message, data] : [message];
|
|
324
|
-
switch (level
|
|
324
|
+
switch (level) {
|
|
325
325
|
case LogLevel.ERROR:
|
|
326
326
|
console.error("[SnackBase]", ...args);
|
|
327
327
|
break;
|
|
@@ -568,21 +568,29 @@ var AuthService = class {
|
|
|
568
568
|
const data = { ...credentials };
|
|
569
569
|
if (!data.account && this.defaultAccount) data.account = this.defaultAccount;
|
|
570
570
|
const authData = (await this.http.post("/api/v1/auth/login", data)).data;
|
|
571
|
+
console.log("Login Response:", JSON.stringify(authData, null, 2));
|
|
572
|
+
const refreshToken = authData.refresh_token || authData.refreshToken;
|
|
573
|
+
let expiresAt = authData.expiresAt;
|
|
574
|
+
if (authData.expires_in && !expiresAt) expiresAt = new Date(Date.now() + authData.expires_in * 1e3).toISOString();
|
|
571
575
|
await this.auth.setState({
|
|
572
|
-
user: authData.user,
|
|
573
|
-
account: authData.account,
|
|
574
|
-
token: authData.token,
|
|
575
|
-
refreshToken:
|
|
576
|
-
expiresAt:
|
|
576
|
+
user: authData.user || null,
|
|
577
|
+
account: authData.account || null,
|
|
578
|
+
token: authData.token || null,
|
|
579
|
+
refreshToken: refreshToken || null,
|
|
580
|
+
expiresAt: expiresAt || null
|
|
577
581
|
});
|
|
578
|
-
return
|
|
582
|
+
return {
|
|
583
|
+
...authData,
|
|
584
|
+
refreshToken,
|
|
585
|
+
expiresAt
|
|
586
|
+
};
|
|
579
587
|
}
|
|
580
588
|
/**
|
|
581
589
|
* Register a new user and account.
|
|
582
590
|
*/
|
|
583
591
|
async register(data) {
|
|
584
592
|
const payload = { ...data };
|
|
585
|
-
if (!payload.
|
|
593
|
+
if (!payload.account_name && this.defaultAccount) payload.account_name = this.defaultAccount;
|
|
586
594
|
return (await this.http.post("/api/v1/auth/register", payload)).data;
|
|
587
595
|
}
|
|
588
596
|
/**
|
|
@@ -591,15 +599,20 @@ var AuthService = class {
|
|
|
591
599
|
async refreshToken() {
|
|
592
600
|
const refreshToken = this.auth.refreshToken;
|
|
593
601
|
if (!refreshToken) throw new Error("No refresh token available");
|
|
594
|
-
const authData = (await this.http.post("/api/v1/auth/refresh", { refreshToken })).data;
|
|
602
|
+
const authData = (await this.http.post("/api/v1/auth/refresh", { refresh_token: refreshToken })).data;
|
|
603
|
+
const newRefreshToken = authData.refresh_token || authData.refreshToken;
|
|
604
|
+
let expiresAt = authData.expiresAt;
|
|
605
|
+
if (authData.expires_in && !expiresAt) expiresAt = new Date(Date.now() + authData.expires_in * 1e3).toISOString();
|
|
595
606
|
await this.auth.setState({
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
refreshToken: authData.refreshToken,
|
|
600
|
-
expiresAt: authData.expiresAt
|
|
607
|
+
token: authData.token || null,
|
|
608
|
+
refreshToken: newRefreshToken || null,
|
|
609
|
+
expiresAt: expiresAt || null
|
|
601
610
|
});
|
|
602
|
-
return
|
|
611
|
+
return {
|
|
612
|
+
...authData,
|
|
613
|
+
refreshToken: newRefreshToken,
|
|
614
|
+
expiresAt
|
|
615
|
+
};
|
|
603
616
|
}
|
|
604
617
|
/**
|
|
605
618
|
* Log out the current user.
|
|
@@ -617,11 +630,31 @@ var AuthService = class {
|
|
|
617
630
|
*/
|
|
618
631
|
async getCurrentUser() {
|
|
619
632
|
const authData = (await this.http.get("/api/v1/auth/me")).data;
|
|
633
|
+
console.log("GetMe Response:", JSON.stringify(authData, null, 2));
|
|
634
|
+
const user = authData.user || (authData.user_id ? {
|
|
635
|
+
id: authData.user_id,
|
|
636
|
+
email: authData.email || "",
|
|
637
|
+
role: authData.role || "user",
|
|
638
|
+
groups: [],
|
|
639
|
+
is_active: true,
|
|
640
|
+
created_at: "",
|
|
641
|
+
last_login: null
|
|
642
|
+
} : null);
|
|
643
|
+
const account = authData.account || (authData.account_id ? {
|
|
644
|
+
id: authData.account_id,
|
|
645
|
+
slug: "",
|
|
646
|
+
name: "",
|
|
647
|
+
created_at: ""
|
|
648
|
+
} : null);
|
|
620
649
|
await this.auth.setState({
|
|
621
|
-
user
|
|
622
|
-
account
|
|
650
|
+
user,
|
|
651
|
+
account
|
|
623
652
|
});
|
|
624
|
-
return
|
|
653
|
+
return {
|
|
654
|
+
...authData,
|
|
655
|
+
user: user || void 0,
|
|
656
|
+
account: account || void 0
|
|
657
|
+
};
|
|
625
658
|
}
|
|
626
659
|
/**
|
|
627
660
|
* Initiate password reset flow.
|
|
@@ -907,6 +940,62 @@ var CollectionService = class {
|
|
|
907
940
|
await this.http.delete(`/api/v1/collections/${collectionId}`);
|
|
908
941
|
return { success: true };
|
|
909
942
|
}
|
|
943
|
+
/**
|
|
944
|
+
* Export collections to JSON format.
|
|
945
|
+
* Returns collection schemas and rules for backup or migration.
|
|
946
|
+
*
|
|
947
|
+
* @param params Optional filter by collection IDs
|
|
948
|
+
* @returns Complete export data structure with collections, schemas, and rules
|
|
949
|
+
* @throws {AuthorizationError} If user is not a superadmin
|
|
950
|
+
*
|
|
951
|
+
* @example
|
|
952
|
+
* // Export all collections
|
|
953
|
+
* const exportData = await client.collections.export();
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* // Export specific collections
|
|
957
|
+
* const exportData = await client.collections.export({
|
|
958
|
+
* collection_ids: ['col-123', 'col-456']
|
|
959
|
+
* });
|
|
960
|
+
*/
|
|
961
|
+
async export(params) {
|
|
962
|
+
const queryParams = {};
|
|
963
|
+
if (params?.collection_ids && params.collection_ids.length > 0) queryParams.collection_ids = params.collection_ids.join(",");
|
|
964
|
+
return (await this.http.get("/api/v1/collections/export", { params: queryParams })).data;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Import collections from JSON export.
|
|
968
|
+
*
|
|
969
|
+
* @param request Import request with data and conflict strategy
|
|
970
|
+
* @returns Import result with per-collection status and migration IDs
|
|
971
|
+
* @throws {ValidationError} If import data is invalid
|
|
972
|
+
* @throws {ConflictError} If collection exists and strategy is 'error'
|
|
973
|
+
* @throws {AuthorizationError} If user is not a superadmin
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* // Import with error strategy (fail on conflicts)
|
|
977
|
+
* const result = await client.collections.import({
|
|
978
|
+
* data: exportData,
|
|
979
|
+
* strategy: 'error'
|
|
980
|
+
* });
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* // Import with skip strategy (skip existing collections)
|
|
984
|
+
* const result = await client.collections.import({
|
|
985
|
+
* data: exportData,
|
|
986
|
+
* strategy: 'skip'
|
|
987
|
+
* });
|
|
988
|
+
*
|
|
989
|
+
* @example
|
|
990
|
+
* // Import with update strategy (update existing collections)
|
|
991
|
+
* const result = await client.collections.import({
|
|
992
|
+
* data: exportData,
|
|
993
|
+
* strategy: 'update'
|
|
994
|
+
* });
|
|
995
|
+
*/
|
|
996
|
+
async import(request) {
|
|
997
|
+
return (await this.http.post("/api/v1/collections/import", request)).data;
|
|
998
|
+
}
|
|
910
999
|
};
|
|
911
1000
|
|
|
912
1001
|
//#endregion
|
|
@@ -1090,7 +1179,14 @@ var RecordService = class {
|
|
|
1090
1179
|
if (params.sort !== void 0) formattedParams.sort = params.sort;
|
|
1091
1180
|
if (params.fields) formattedParams.fields = Array.isArray(params.fields) ? params.fields.join(",") : params.fields;
|
|
1092
1181
|
if (params.expand) formattedParams.expand = Array.isArray(params.expand) ? params.expand.join(",") : params.expand;
|
|
1093
|
-
if (params.filter)
|
|
1182
|
+
if (params.filter) {
|
|
1183
|
+
const filterStr = typeof params.filter === "string" ? params.filter : JSON.stringify(params.filter);
|
|
1184
|
+
const match = filterStr.match(/^\(?\s*(\w+)\s*=\s*(["']?)([^"'\)]+)\2\s*\)?$/);
|
|
1185
|
+
if (match) {
|
|
1186
|
+
const [, key, , value] = match;
|
|
1187
|
+
formattedParams[key] = value;
|
|
1188
|
+
} else formattedParams.filter = filterStr;
|
|
1189
|
+
}
|
|
1094
1190
|
}
|
|
1095
1191
|
return (await this.http.get(`/api/v1/records/${collection}`, { params: formattedParams })).data;
|
|
1096
1192
|
}
|
|
@@ -1325,7 +1421,25 @@ var AuditLogService = class {
|
|
|
1325
1421
|
return (await this.httpClient.get(`/api/v1/audit-logs/${logId}`)).data;
|
|
1326
1422
|
}
|
|
1327
1423
|
/**
|
|
1328
|
-
* Exports audit logs in the specified format.
|
|
1424
|
+
* Exports audit logs in the specified format (JSON, CSV, or PDF).
|
|
1425
|
+
*
|
|
1426
|
+
* @param params Optional filters (account_id, table_name, operation, date range, etc.)
|
|
1427
|
+
* @param format Export format: 'json', 'csv', or 'pdf' (default: 'json')
|
|
1428
|
+
* @returns Exported data as string (base64-encoded for PDF format)
|
|
1429
|
+
* @throws {AuthorizationError} If user is not a superadmin
|
|
1430
|
+
*
|
|
1431
|
+
* @example
|
|
1432
|
+
* // Export as JSON
|
|
1433
|
+
* const jsonData = await client.auditLogs.export({ table_name: 'users' }, 'json');
|
|
1434
|
+
*
|
|
1435
|
+
* @example
|
|
1436
|
+
* // Export as CSV
|
|
1437
|
+
* const csvData = await client.auditLogs.export({ table_name: 'users' }, 'csv');
|
|
1438
|
+
*
|
|
1439
|
+
* @example
|
|
1440
|
+
* // Export as PDF
|
|
1441
|
+
* const pdfBase64 = await client.auditLogs.export({ table_name: 'users' }, 'pdf');
|
|
1442
|
+
* // pdfBase64 is a base64-encoded PDF string
|
|
1329
1443
|
*/
|
|
1330
1444
|
async export(params, format = "json") {
|
|
1331
1445
|
return (await this.httpClient.get("/api/v1/audit-logs/export", { params: {
|
package/package.json
CHANGED
|
@@ -1,48 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snackbase/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Core SDK for SnackBase",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.cts",
|
|
5
8
|
"exports": {
|
|
6
9
|
".": {
|
|
7
|
-
"import":
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
15
18
|
}
|
|
16
19
|
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"snackbase",
|
|
25
|
+
"sdk",
|
|
26
|
+
"database"
|
|
27
|
+
],
|
|
28
|
+
"author": "SnackBase Team",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@snackbase/tsconfig": "0.1.0"
|
|
32
|
+
},
|
|
17
33
|
"scripts": {
|
|
18
|
-
"dev": "tsdown src/index.ts
|
|
19
|
-
"build": "tsdown src/index.ts
|
|
34
|
+
"dev": "tsdown src/index.ts --format esm,cjs --watch",
|
|
35
|
+
"build": "tsdown src/index.ts --format esm,cjs --dts",
|
|
20
36
|
"test": "vitest run",
|
|
21
|
-
"test:watch": "vitest",
|
|
22
|
-
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
23
|
-
"test:coverage": "vitest run --coverage",
|
|
24
37
|
"typecheck": "tsc --noEmit",
|
|
25
|
-
"
|
|
26
|
-
"prepublishOnly": "npm run build && npm run test"
|
|
27
|
-
},
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"react": "^18.0.0"
|
|
30
|
-
},
|
|
31
|
-
"peerDependenciesMeta": {
|
|
32
|
-
"react": {
|
|
33
|
-
"optional": true
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
38
|
-
"@testing-library/react": "^16.3.2",
|
|
39
|
-
"@vitejs/plugin-react": "^5.1.2",
|
|
40
|
-
"jsdom": "^27.4.0",
|
|
41
|
-
"react": "^18.2.0",
|
|
42
|
-
"react-dom": "^18.2.0",
|
|
43
|
-
"tsdown": "^0.20.0-beta.4",
|
|
44
|
-
"typescript": "^5.3.3",
|
|
45
|
-
"vite": "^7.3.1",
|
|
46
|
-
"vitest": "^1.2.1"
|
|
38
|
+
"clean": "rm -rf dist"
|
|
47
39
|
}
|
|
48
|
-
}
|
|
40
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [Unreleased]
|
|
9
|
-
|
|
10
|
-
### Added
|
|
11
|
-
- Initial release of SnackBase SDK for JavaScript/TypeScript
|
|
12
|
-
|
|
13
|
-
### Features
|
|
14
|
-
- **Core Client**: `SnackBaseClient` with configuration validation
|
|
15
|
-
- **HTTP Client**: Fetch-based HTTP client with interceptors
|
|
16
|
-
- **Authentication**: Email/password, OAuth, SAML, and API key authentication
|
|
17
|
-
- **Services**: 17+ service classes for all SnackBase resources
|
|
18
|
-
- Accounts, Users, Collections, Records
|
|
19
|
-
- Roles, Collection Rules (Permission System V2)
|
|
20
|
-
- Groups, Invitations, Macros
|
|
21
|
-
- API Keys, Audit Logs, Dashboard, Admin
|
|
22
|
-
- Email Templates, Files
|
|
23
|
-
- **Real-Time**: WebSocket/SSE support with automatic reconnection
|
|
24
|
-
- **Query Builder**: Fluent API for complex queries
|
|
25
|
-
- **React Integration**: Context provider and hooks
|
|
26
|
-
- `useAuth`, `useQuery`, `useRecord`, `useMutation`, `useSubscription`
|
|
27
|
-
- **Type Safety**: Complete TypeScript definitions
|
|
28
|
-
- **Error Handling**: Typed error hierarchy
|
|
29
|
-
- **Storage Abstraction**: Platform-agnostic storage backends
|
|
30
|
-
- **Logging**: Structured logging system with configurable levels
|
|
31
|
-
|
|
32
|
-
### Package Exports
|
|
33
|
-
- `@snackbase/sdk` - Core SDK
|
|
34
|
-
- `@snackbase/sdk/react` - React integration
|
|
35
|
-
|
|
36
|
-
### Build Output
|
|
37
|
-
- ESM (`.mjs`) - 14.87 KB gzipped
|
|
38
|
-
- CommonJS (`.js`)
|
|
39
|
-
- TypeScript declarations (`.d.ts`)
|
|
40
|
-
|
|
41
|
-
## [0.1.0] - 2025-01-XX
|
|
42
|
-
|
|
43
|
-
### Added
|
|
44
|
-
- Initial beta release
|
|
45
|
-
- Complete Phase 1-4 implementation
|
|
46
|
-
- Core SDK features
|
|
47
|
-
- React hooks
|
|
48
|
-
- Real-time subscriptions
|
|
49
|
-
- Query builder
|
|
50
|
-
- 231 tests passing
|
|
51
|
-
|
|
52
|
-
## [Future Releases]
|
|
53
|
-
|
|
54
|
-
### Planned
|
|
55
|
-
- Integration tests with test server
|
|
56
|
-
- Performance benchmarks
|
|
57
|
-
- Request deduplication
|
|
58
|
-
- Advanced caching strategies
|
|
59
|
-
- Vue 3 integration
|
|
60
|
-
- Angular integration
|
|
61
|
-
- Svelte integration
|
package/README.md
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
# SnackBase SDK
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@snackbase/sdk)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/snackbase/snackbase-js/actions)
|
|
6
|
-
|
|
7
|
-
The official JavaScript/TypeScript SDK for [SnackBase](https://snackbase.dev) - a powerful backend-as-a-service platform.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- **Type-Safe**: Built with TypeScript for full type safety and excellent developer experience
|
|
12
|
-
- **Authentication**: Support for JWT tokens, OAuth, SAML, and API keys
|
|
13
|
-
- **Real-Time**: WebSocket and SSE support for real-time data synchronization
|
|
14
|
-
- **React Integration**: Dedicated React hooks and context for seamless integration
|
|
15
|
-
- **Query Builder**: Fluent API for building complex queries with filtering, sorting, and pagination
|
|
16
|
-
- **Platform Agnostic**: Works in browsers, React Native, and Node.js
|
|
17
|
-
- **Lightweight**: Only 14.87 KB gzipped for the core SDK
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# npm
|
|
23
|
-
npm install @snackbase/sdk
|
|
24
|
-
|
|
25
|
-
# yarn
|
|
26
|
-
yarn add @snackbase/sdk
|
|
27
|
-
|
|
28
|
-
# pnpm
|
|
29
|
-
pnpm add @snackbase/sdk
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### React Integration
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# npm
|
|
36
|
-
npm install @snackbase/sdk react
|
|
37
|
-
|
|
38
|
-
# yarn
|
|
39
|
-
yarn add @snackbase/sdk react
|
|
40
|
-
|
|
41
|
-
# pnpm
|
|
42
|
-
pnpm add @snackbase/sdk react
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Quick Start
|
|
46
|
-
|
|
47
|
-
### Basic Usage
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
import { SnackBaseClient } from "@snackbase/sdk";
|
|
51
|
-
|
|
52
|
-
// Initialize the client
|
|
53
|
-
const client = new SnackBaseClient({
|
|
54
|
-
baseUrl: "https://your-project.snackbase.dev",
|
|
55
|
-
apiKey: "your-api-key", // Optional for public access
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Authenticate with email/password
|
|
59
|
-
const auth = await client.auth.authenticateWithPassword({
|
|
60
|
-
email: "user@example.com",
|
|
61
|
-
password: "password123",
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
console.log("Logged in as:", auth.user.email);
|
|
65
|
-
|
|
66
|
-
// List records from a collection
|
|
67
|
-
const records = await client.records.list("posts", {
|
|
68
|
-
filter: { status: "published" },
|
|
69
|
-
sort: "-createdAt",
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Subscribe to real-time updates
|
|
73
|
-
client.realtime.subscribe("posts", (event) => {
|
|
74
|
-
console.log("New event:", event.action, event.record);
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### React Integration
|
|
79
|
-
|
|
80
|
-
```tsx
|
|
81
|
-
import { SnackBaseProvider, useAuth, useRecord } from "@snackbase/sdk/react";
|
|
82
|
-
|
|
83
|
-
function App() {
|
|
84
|
-
return (
|
|
85
|
-
<SnackBaseProvider
|
|
86
|
-
baseUrl="https://your-project.snackbase.dev"
|
|
87
|
-
apiKey="your-api-key"
|
|
88
|
-
>
|
|
89
|
-
<Posts />
|
|
90
|
-
</SnackBaseProvider>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function Posts() {
|
|
95
|
-
const { user, login, logout } = useAuth();
|
|
96
|
-
const { data: posts, loading } = useRecord("posts", {
|
|
97
|
-
filter: { status: "published" },
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
if (!user) {
|
|
101
|
-
return <button onClick={() => login(email, password)}>Login</button>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div>
|
|
106
|
-
<button onClick={logout}>Logout</button>
|
|
107
|
-
{loading ? <p>Loading...</p> : <PostList posts={posts?.items} />}
|
|
108
|
-
</div>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Documentation
|
|
114
|
-
|
|
115
|
-
- [Getting Started Guide](./docs/getting-started.md)
|
|
116
|
-
- [Authentication Guide](./docs/authentication.md)
|
|
117
|
-
- [Real-Time Features](./docs/realtime.md)
|
|
118
|
-
- [React Integration](./docs/react-integration.md)
|
|
119
|
-
- [API Reference](./docs/api-reference.md)
|
|
120
|
-
- [Migration Guides](./docs/migration/)
|
|
121
|
-
|
|
122
|
-
## Platform Support
|
|
123
|
-
|
|
124
|
-
| Platform | Support |
|
|
125
|
-
| --------------------------------------- | ------- |
|
|
126
|
-
| Browser (Chrome, Firefox, Safari, Edge) | ✅ Full |
|
|
127
|
-
| React Native | ✅ Full |
|
|
128
|
-
| Node.js | ✅ Full |
|
|
129
|
-
| Next.js | ✅ Full |
|
|
130
|
-
| Vue/Nuxt | ✅ Full |
|
|
131
|
-
|
|
132
|
-
## Authentication Methods
|
|
133
|
-
|
|
134
|
-
| Method | Description |
|
|
135
|
-
| ------------------ | -------------------------------------------------- |
|
|
136
|
-
| **Email/Password** | Traditional authentication with email and password |
|
|
137
|
-
| **OAuth** | Sign in with Google, GitHub, etc. |
|
|
138
|
-
| **SAML** | Enterprise SSO support |
|
|
139
|
-
| **API Key** | Server-to-server authentication |
|
|
140
|
-
|
|
141
|
-
## Core Services
|
|
142
|
-
|
|
143
|
-
The SDK provides 17+ services for interacting with SnackBase:
|
|
144
|
-
|
|
145
|
-
- `client.auth` - Authentication and user management
|
|
146
|
-
- `client.users` - User CRUD operations
|
|
147
|
-
- `client.accounts` - Account management
|
|
148
|
-
- `client.collections` - Collection/schema management
|
|
149
|
-
- `client.records` - Dynamic record operations (CRUD, queries)
|
|
150
|
-
- `client.roles` - Role-based access control
|
|
151
|
-
- `client.groups` - Group management
|
|
152
|
-
- `client.invitations` - User invitations
|
|
153
|
-
- `client.macros` - Macro operations
|
|
154
|
-
- `client.apiKeys` - API key management
|
|
155
|
-
- `client.auditLogs` - Audit log access
|
|
156
|
-
- `client.dashboard` - Dashboard metrics
|
|
157
|
-
- `client.admin` - Admin operations
|
|
158
|
-
- `client.emailTemplates` - Email template management
|
|
159
|
-
- `client.files` - File upload/download
|
|
160
|
-
- `client.realtime` - Real-time subscriptions
|
|
161
|
-
- `client.query` - Query builder
|
|
162
|
-
|
|
163
|
-
## Query Builder
|
|
164
|
-
|
|
165
|
-
Build complex queries with a fluent API:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const results = await client
|
|
169
|
-
.query("posts")
|
|
170
|
-
.select("id", "title", "author.name")
|
|
171
|
-
.expand("author", "comments")
|
|
172
|
-
.filter("status", "=", "published")
|
|
173
|
-
.filter("createdAt", ">", "2024-01-01")
|
|
174
|
-
.sort("createdAt", "desc")
|
|
175
|
-
.page(1)
|
|
176
|
-
.perPage(20)
|
|
177
|
-
.execute();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Real-Time Subscriptions
|
|
181
|
-
|
|
182
|
-
Subscribe to record changes in real-time:
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
// Subscribe to a collection
|
|
186
|
-
const unsubscribe = client.realtime.subscribe("posts", (event) => {
|
|
187
|
-
switch (event.action) {
|
|
188
|
-
case "create":
|
|
189
|
-
console.log("New post created:", event.record);
|
|
190
|
-
break;
|
|
191
|
-
case "update":
|
|
192
|
-
console.log("Post updated:", event.record);
|
|
193
|
-
break;
|
|
194
|
-
case "delete":
|
|
195
|
-
console.log("Post deleted:", event.record);
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Unsubscribe when done
|
|
201
|
-
unsubscribe();
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Error Handling
|
|
205
|
-
|
|
206
|
-
The SDK provides typed errors for comprehensive error handling:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
import {
|
|
210
|
-
SnackBaseError,
|
|
211
|
-
AuthenticationError,
|
|
212
|
-
ValidationError,
|
|
213
|
-
NetworkError,
|
|
214
|
-
} from "@snackbase/sdk";
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
await client.records.create("posts", data);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
if (error instanceof ValidationError) {
|
|
220
|
-
console.error("Validation failed:", error.fields);
|
|
221
|
-
} else if (error instanceof AuthenticationError) {
|
|
222
|
-
console.error("Authentication failed:", error.message);
|
|
223
|
-
} else if (error instanceof NetworkError) {
|
|
224
|
-
console.error("Network error:", error.message);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## TypeScript Support
|
|
230
|
-
|
|
231
|
-
The SDK is written in TypeScript and provides full type definitions:
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
import type { User, Post, Comment } from "@snackbase/sdk";
|
|
235
|
-
|
|
236
|
-
// Type-safe record operations
|
|
237
|
-
const post: Post = await client.records.get<Post>("posts", "record-id");
|
|
238
|
-
|
|
239
|
-
// Type-safe auth state
|
|
240
|
-
const user: User = await client.auth.getCurrentUser();
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Development
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
# Clone the repository
|
|
247
|
-
git clone https://github.com/snackbase/snackbase-js.git
|
|
248
|
-
cd snackbase-js
|
|
249
|
-
|
|
250
|
-
# Install dependencies
|
|
251
|
-
npm install
|
|
252
|
-
|
|
253
|
-
# Build
|
|
254
|
-
npm run build
|
|
255
|
-
|
|
256
|
-
# Run tests
|
|
257
|
-
npm test
|
|
258
|
-
|
|
259
|
-
# Watch mode
|
|
260
|
-
npm run dev
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## Bundle Size
|
|
264
|
-
|
|
265
|
-
- Core SDK: **14.87 KB** (gzipped)
|
|
266
|
-
- React Integration: **1.42 KB** (gzipped)
|
|
267
|
-
- Total: **16.29 KB** (gzipped)
|
|
268
|
-
|
|
269
|
-
## License
|
|
270
|
-
|
|
271
|
-
MIT © [SnackBase](https://snackbase.dev)
|
|
272
|
-
|
|
273
|
-
## Contributing
|
|
274
|
-
|
|
275
|
-
We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
|
|
276
|
-
|
|
277
|
-
## Support
|
|
278
|
-
|
|
279
|
-
- Documentation: [https://docs.snackbase.dev](https://docs.snackbase.dev)
|
|
280
|
-
- GitHub Issues: [https://github.com/snackbase/snackbase-js/issues](https://github.com/snackbase/snackbase-js/issues)
|
|
281
|
-
- Discord: [https://discord.gg/snackbase](https://discord.gg/snackbase)
|
|
282
|
-
|
|
283
|
-
## Related Projects
|
|
284
|
-
|
|
285
|
-
- [SnackBase](https://snackbase.dev) - The official SnackBase platform
|
|
286
|
-
- [snackbase-python](https://github.com/snackbase/snackbase-python) - Python SDK
|
|
287
|
-
- [snackbase-go](https://github.com/snackbase/snackbase-go) - Go SDK
|