@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.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: level$1, message, data } = entry;
322
+ const { level, message, data } = entry;
323
323
  const args = data ? [message, data] : [message];
324
- switch (level$1) {
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: authData.refreshToken,
576
- expiresAt: authData.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 authData;
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.accountName && this.defaultAccount) payload.accountName = this.defaultAccount;
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
- user: authData.user,
597
- account: authData.account,
598
- token: authData.token,
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 authData;
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: authData.user,
622
- account: authData.account
650
+ user,
651
+ account
623
652
  });
624
- return authData;
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) formattedParams.filter = typeof params.filter === "string" ? params.filter : JSON.stringify(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.1.0",
4
- "description": "JavaScript/TypeScript SDK for SnackBase",
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": "./dist/index.mjs",
8
- "require": "./dist/index.js",
9
- "types": "./dist/index.d.ts"
10
- },
11
- "./react": {
12
- "import": "./dist/react/index.mjs",
13
- "require": "./dist/react/index.js",
14
- "types": "./dist/react/index.d.ts"
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 src/react/index.ts --watch",
19
- "build": "tsdown src/index.ts src/react/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
- "lint": "eslint src --ext .ts,.tsx",
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
- [![npm version](https://badge.fury.io/js/%40snackbase%2Fsdk.svg)](https://www.npmjs.com/package/@snackbase/sdk)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Build Status](https://github.com/snackbase/snackbase-js/workflows/CI/badge.svg)](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