corebase-js 0.1.0 → 0.1.1
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 +145 -24
- package/dist/auth.d.ts +5 -13
- package/dist/client.d.ts +2 -0
- package/dist/client.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/query.d.ts +42 -0
- package/dist/query.js +141 -0
- package/dist/types.d.ts +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CoreBase JS Client
|
|
2
2
|
|
|
3
|
-
A JavaScript/TypeScript client for CoreBase.
|
|
3
|
+
A robust JavaScript/TypeScript client for communicating with your CoreBase backend. This library provides a simple, Supabase-like interface for authentication and database operations.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,60 +10,181 @@ npm install corebase-js
|
|
|
10
10
|
|
|
11
11
|
## Initialization
|
|
12
12
|
|
|
13
|
-
Initialize the client with your project URL and API Key.
|
|
13
|
+
Initialize the client with your CoreBase project URL and API Key.
|
|
14
14
|
|
|
15
|
-
```
|
|
15
|
+
```typescript
|
|
16
16
|
import { createClient } from 'corebase-js';
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const CLIENT_URL = 'https://corebase.trivyaa.in'; // Your CoreBase API URL
|
|
19
|
+
const API_KEY = 'pk_...'; // Your Project Public API Key
|
|
20
|
+
|
|
21
|
+
const corebase = createClient(CLIENT_URL, API_KEY);
|
|
19
22
|
```
|
|
20
23
|
|
|
21
24
|
## Authentication
|
|
22
25
|
|
|
26
|
+
Manage user sessions securely.
|
|
27
|
+
|
|
23
28
|
### Sign Up
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
Register a new user in your project.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
26
33
|
const { data, error } = await corebase.auth.signUp({
|
|
27
34
|
email: 'user@example.com',
|
|
28
|
-
password: '
|
|
35
|
+
password: 'securePassword123',
|
|
29
36
|
name: 'John Doe'
|
|
30
37
|
});
|
|
31
38
|
|
|
32
|
-
if (error)
|
|
33
|
-
|
|
34
|
-
} else {
|
|
35
|
-
console.log('User created:', data.user);
|
|
36
|
-
}
|
|
39
|
+
if (error) console.error('Signup error:', error);
|
|
40
|
+
else console.log('Welcome!', data.user);
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
### Sign In
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
Log in an existing user. The session is automatically persisted (in localStorage for browsers).
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
42
48
|
const { data, error } = await corebase.auth.signIn({
|
|
43
49
|
email: 'user@example.com',
|
|
44
|
-
password: '
|
|
50
|
+
password: 'securePassword123'
|
|
45
51
|
});
|
|
46
52
|
|
|
47
|
-
if (error)
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
console.log('Logged in user:', data.user);
|
|
51
|
-
console.log('Access token:', data.access_token);
|
|
52
|
-
}
|
|
53
|
+
if (error) console.error('Login error:', error);
|
|
54
|
+
else console.log('Logged in:', data.user);
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
### Get Current User
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
Retrieve the currently authenticated user's details.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
58
62
|
const { data, error } = await corebase.auth.getUser();
|
|
59
63
|
|
|
60
|
-
if (data)
|
|
61
|
-
console.log('Current user:', data.user);
|
|
62
|
-
}
|
|
64
|
+
if (data) console.log('Current user:', data.user);
|
|
63
65
|
```
|
|
64
66
|
|
|
65
67
|
### Sign Out
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
Clear the current session.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
68
72
|
await corebase.auth.signOut();
|
|
69
73
|
```
|
|
74
|
+
|
|
75
|
+
## specific Database Operations
|
|
76
|
+
|
|
77
|
+
Perform CRUD operations on your tables using a fluent, chainable API.
|
|
78
|
+
|
|
79
|
+
### Select Data (Read)
|
|
80
|
+
|
|
81
|
+
**Get all rows:**
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const { data, error, count } = await corebase
|
|
85
|
+
.from('posts')
|
|
86
|
+
.select('*');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Select specific columns:**
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const { data } = await corebase
|
|
93
|
+
.from('users')
|
|
94
|
+
.select('id, name, email');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Filtering (Where clause):**
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const { data } = await corebase
|
|
101
|
+
.from('posts')
|
|
102
|
+
.select('*')
|
|
103
|
+
.eq('status', 'published')
|
|
104
|
+
.match({ author_id: 'user_123', category: 'tech' });
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Pagination & Sorting:**
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const { data, count } = await corebase
|
|
111
|
+
.from('posts')
|
|
112
|
+
.select('*')
|
|
113
|
+
.order('created_at', { ascending: false }) // Sort by newest
|
|
114
|
+
.limit(10) // Take 10
|
|
115
|
+
.page(1); // Page 1
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Get a Single Record:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const { data: user, error } = await corebase
|
|
122
|
+
.from('users')
|
|
123
|
+
.select('*')
|
|
124
|
+
.eq('id', 1)
|
|
125
|
+
.single();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Insert Data (Create)
|
|
129
|
+
|
|
130
|
+
Insert one or multiple rows.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Single insert
|
|
134
|
+
const { data, error } = await corebase
|
|
135
|
+
.from('posts')
|
|
136
|
+
.insert({
|
|
137
|
+
title: 'My New Post',
|
|
138
|
+
content: 'Hello World',
|
|
139
|
+
user_id: 'user_123'
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Bulk insert
|
|
143
|
+
const { data, error } = await corebase
|
|
144
|
+
.from('posts')
|
|
145
|
+
.insert([
|
|
146
|
+
{ title: 'Post 1', user_id: 'user_123' },
|
|
147
|
+
{ title: 'Post 2', user_id: 'user_123' }
|
|
148
|
+
]);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Update Data
|
|
152
|
+
|
|
153
|
+
Update rows matching specific criteria.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const { data, error } = await corebase
|
|
157
|
+
.from('posts')
|
|
158
|
+
.update({ status: 'archived' })
|
|
159
|
+
.eq('status', 'draft')
|
|
160
|
+
.match({ user_id: 'user_123' });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Delete Data
|
|
164
|
+
|
|
165
|
+
Delete rows matching specific criteria.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const { data, error } = await corebase
|
|
169
|
+
.from('posts')
|
|
170
|
+
.delete()
|
|
171
|
+
.eq('id', 123);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## TypeScript Support
|
|
175
|
+
|
|
176
|
+
This library is written in TypeScript and exports types for all responses.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { User, AuthSession, ClientResponse } from 'corebase-js';
|
|
180
|
+
|
|
181
|
+
// You can also provide a generic type to .from() for typed results
|
|
182
|
+
interface Post {
|
|
183
|
+
id: number;
|
|
184
|
+
title: string;
|
|
185
|
+
content: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const { data } = await corebase.from<Post>('posts').select('*');
|
|
189
|
+
// data is typed as Post[] | null
|
|
190
|
+
```
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import { AuthSession, ClientConfig, User } from './types';
|
|
2
|
-
export interface AuthResponse<T> {
|
|
3
|
-
data: T | null;
|
|
4
|
-
error: {
|
|
5
|
-
message: string;
|
|
6
|
-
code?: string;
|
|
7
|
-
details?: string;
|
|
8
|
-
} | null;
|
|
9
|
-
}
|
|
1
|
+
import { AuthSession, ClientConfig, ClientResponse, User } from './types';
|
|
10
2
|
export declare class AuthClient {
|
|
11
3
|
private config;
|
|
12
4
|
private session;
|
|
@@ -14,19 +6,19 @@ export declare class AuthClient {
|
|
|
14
6
|
constructor(config: ClientConfig);
|
|
15
7
|
private get baseUrl();
|
|
16
8
|
private get headers();
|
|
17
|
-
|
|
9
|
+
request<T>(path: string, options?: RequestInit): Promise<ClientResponse<T>>;
|
|
18
10
|
signUp(credentials: {
|
|
19
11
|
email: string;
|
|
20
12
|
password: string;
|
|
21
13
|
name?: string;
|
|
22
|
-
}): Promise<
|
|
14
|
+
}): Promise<ClientResponse<{
|
|
23
15
|
user: User;
|
|
24
16
|
}>>;
|
|
25
17
|
signIn(credentials: {
|
|
26
18
|
email: string;
|
|
27
19
|
password: string;
|
|
28
|
-
}): Promise<
|
|
29
|
-
getUser(): Promise<
|
|
20
|
+
}): Promise<ClientResponse<AuthSession>>;
|
|
21
|
+
getUser(): Promise<ClientResponse<{
|
|
30
22
|
user: User;
|
|
31
23
|
}>>;
|
|
32
24
|
signOut(): Promise<{
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { AuthClient } from './auth';
|
|
2
|
+
import { QueryBuilder } from './query';
|
|
2
3
|
import { ClientConfig } from './types';
|
|
3
4
|
export declare class CoreBaseClient {
|
|
4
5
|
auth: AuthClient;
|
|
5
6
|
private config;
|
|
6
7
|
constructor(config: ClientConfig);
|
|
8
|
+
from<T = any>(table: string): QueryBuilder<T>;
|
|
7
9
|
}
|
|
8
10
|
export declare function createClient(baseUrl: string, apiKey: string): CoreBaseClient;
|
package/dist/client.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CoreBaseClient = void 0;
|
|
4
4
|
exports.createClient = createClient;
|
|
5
5
|
const auth_1 = require("./auth");
|
|
6
|
+
const query_1 = require("./query");
|
|
6
7
|
class CoreBaseClient {
|
|
7
8
|
constructor(config) {
|
|
8
9
|
if (!config.apiKey) {
|
|
@@ -14,6 +15,9 @@ class CoreBaseClient {
|
|
|
14
15
|
this.config = config;
|
|
15
16
|
this.auth = new auth_1.AuthClient(config);
|
|
16
17
|
}
|
|
18
|
+
from(table) {
|
|
19
|
+
return new query_1.QueryBuilder((path, options) => this.auth.request(path, options), table);
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
exports.CoreBaseClient = CoreBaseClient;
|
|
19
23
|
function createClient(baseUrl, apiKey) {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ClientResponse } from './types';
|
|
2
|
+
export type RequestHandler = <T>(path: string, options?: RequestInit) => Promise<ClientResponse<T>>;
|
|
3
|
+
export declare class QueryBuilder<T = any> {
|
|
4
|
+
private request;
|
|
5
|
+
private tableName;
|
|
6
|
+
constructor(request: RequestHandler, tableName: string);
|
|
7
|
+
select(columns?: string): SelectBuilder<T>;
|
|
8
|
+
insert(values: Partial<T> | Partial<T>[]): Promise<ClientResponse<{
|
|
9
|
+
message: string;
|
|
10
|
+
count: number;
|
|
11
|
+
}>>;
|
|
12
|
+
update(values: Partial<T>): FilterBuilder<any>;
|
|
13
|
+
delete(): FilterBuilder<any>;
|
|
14
|
+
}
|
|
15
|
+
export declare class FilterBuilder<T> implements PromiseLike<ClientResponse<T>> {
|
|
16
|
+
protected request: RequestHandler;
|
|
17
|
+
protected tableName: string;
|
|
18
|
+
protected method: 'UPDATE' | 'DELETE' | 'SELECT';
|
|
19
|
+
protected body?: any | undefined;
|
|
20
|
+
protected columns?: string | undefined;
|
|
21
|
+
protected filters: Record<string, any>;
|
|
22
|
+
constructor(request: RequestHandler, tableName: string, method: 'UPDATE' | 'DELETE' | 'SELECT', body?: any | undefined, columns?: string | undefined);
|
|
23
|
+
eq(column: string, value: any): this;
|
|
24
|
+
match(query: Record<string, any>): this;
|
|
25
|
+
then<TResult1 = ClientResponse<T>, TResult2 = never>(onfulfilled?: ((value: ClientResponse<T>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
26
|
+
execute(): Promise<ClientResponse<T>>;
|
|
27
|
+
}
|
|
28
|
+
export declare class SelectBuilder<T> extends FilterBuilder<T> {
|
|
29
|
+
private _limit?;
|
|
30
|
+
private _page?;
|
|
31
|
+
private _sort?;
|
|
32
|
+
private _order?;
|
|
33
|
+
private _isSingle;
|
|
34
|
+
constructor(request: RequestHandler, tableName: string, columns: string);
|
|
35
|
+
limit(count: number): this;
|
|
36
|
+
page(page: number): this;
|
|
37
|
+
order(column: string, { ascending }?: {
|
|
38
|
+
ascending?: boolean;
|
|
39
|
+
}): this;
|
|
40
|
+
single(): this;
|
|
41
|
+
execute(): Promise<ClientResponse<T>>;
|
|
42
|
+
}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SelectBuilder = exports.FilterBuilder = exports.QueryBuilder = void 0;
|
|
13
|
+
class QueryBuilder {
|
|
14
|
+
constructor(request, tableName) {
|
|
15
|
+
this.request = request;
|
|
16
|
+
this.tableName = tableName;
|
|
17
|
+
}
|
|
18
|
+
select(columns = '*') {
|
|
19
|
+
return new SelectBuilder(this.request, this.tableName, columns);
|
|
20
|
+
}
|
|
21
|
+
insert(values) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
return this.request(`/table_operation/insert/${encodeURIComponent(this.tableName)}`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body: JSON.stringify(values),
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
update(values) {
|
|
30
|
+
return new FilterBuilder(this.request, this.tableName, 'UPDATE', values);
|
|
31
|
+
}
|
|
32
|
+
delete() {
|
|
33
|
+
return new FilterBuilder(this.request, this.tableName, 'DELETE');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.QueryBuilder = QueryBuilder;
|
|
37
|
+
class FilterBuilder {
|
|
38
|
+
constructor(request, tableName, method, body, columns) {
|
|
39
|
+
this.request = request;
|
|
40
|
+
this.tableName = tableName;
|
|
41
|
+
this.method = method;
|
|
42
|
+
this.body = body;
|
|
43
|
+
this.columns = columns;
|
|
44
|
+
this.filters = {};
|
|
45
|
+
}
|
|
46
|
+
eq(column, value) {
|
|
47
|
+
this.filters[column] = value;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
match(query) {
|
|
51
|
+
Object.assign(this.filters, query);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
then(onfulfilled, onrejected) {
|
|
55
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
56
|
+
}
|
|
57
|
+
execute() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
if (this.method === 'SELECT') {
|
|
60
|
+
throw new Error("Execute should be called on SelectBuilder");
|
|
61
|
+
}
|
|
62
|
+
const path = `/table_operation/${this.method.toLowerCase()}/${encodeURIComponent(this.tableName)}`;
|
|
63
|
+
let payload = {};
|
|
64
|
+
if (this.method === 'UPDATE') {
|
|
65
|
+
payload = {
|
|
66
|
+
updates: this.body,
|
|
67
|
+
where: this.filters
|
|
68
|
+
};
|
|
69
|
+
return this.request(path, { method: 'PUT', body: JSON.stringify(payload) });
|
|
70
|
+
}
|
|
71
|
+
else if (this.method === 'DELETE') {
|
|
72
|
+
payload = {
|
|
73
|
+
where: this.filters
|
|
74
|
+
};
|
|
75
|
+
return this.request(path, { method: 'DELETE', body: JSON.stringify(payload) });
|
|
76
|
+
}
|
|
77
|
+
return { data: null, error: { message: 'Invalid method' } };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.FilterBuilder = FilterBuilder;
|
|
82
|
+
class SelectBuilder extends FilterBuilder {
|
|
83
|
+
constructor(request, tableName, columns) {
|
|
84
|
+
super(request, tableName, 'SELECT', undefined, columns);
|
|
85
|
+
this._isSingle = false;
|
|
86
|
+
}
|
|
87
|
+
limit(count) {
|
|
88
|
+
this._limit = count;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
page(page) {
|
|
92
|
+
this._page = page;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
order(column, { ascending = true } = {}) {
|
|
96
|
+
this._sort = column;
|
|
97
|
+
this._order = ascending ? 'ASC' : 'DESC';
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
single() {
|
|
101
|
+
this._limit = 1;
|
|
102
|
+
this._isSingle = true;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
execute() {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
var _a, _b, _c;
|
|
108
|
+
const payload = {
|
|
109
|
+
columns: this.columns === '*' ? undefined : (_a = this.columns) === null || _a === void 0 ? void 0 : _a.split(',').map(s => s.trim()),
|
|
110
|
+
where: Object.keys(this.filters).length > 0 ? this.filters : undefined,
|
|
111
|
+
limit: this._limit,
|
|
112
|
+
page: this._page,
|
|
113
|
+
sort: this._sort,
|
|
114
|
+
order: this._order
|
|
115
|
+
};
|
|
116
|
+
const response = yield this.request(`/table_operation/select/${encodeURIComponent(this.tableName)}`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
body: JSON.stringify(payload)
|
|
119
|
+
});
|
|
120
|
+
if (response.error)
|
|
121
|
+
return { data: null, error: response.error };
|
|
122
|
+
const resultData = (_b = response.data) === null || _b === void 0 ? void 0 : _b.data; // Array of items
|
|
123
|
+
const meta = (_c = response.data) === null || _c === void 0 ? void 0 : _c.meta;
|
|
124
|
+
let finalData = resultData;
|
|
125
|
+
if (this._isSingle) {
|
|
126
|
+
if (Array.isArray(resultData) && resultData.length > 0) {
|
|
127
|
+
finalData = resultData[0];
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
finalData = null; // Or undefined? Supabase errors here usually.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
data: finalData,
|
|
135
|
+
error: null,
|
|
136
|
+
count: meta === null || meta === void 0 ? void 0 : meta.total
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.SelectBuilder = SelectBuilder;
|
package/dist/types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface ApiResponse<T> {
|
|
|
15
15
|
data?: T;
|
|
16
16
|
error?: {
|
|
17
17
|
message: string;
|
|
18
|
-
code
|
|
18
|
+
code?: string;
|
|
19
19
|
details?: string;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
@@ -23,3 +23,12 @@ export interface ClientConfig {
|
|
|
23
23
|
apiKey: string;
|
|
24
24
|
baseUrl?: string;
|
|
25
25
|
}
|
|
26
|
+
export interface ClientResponse<T> {
|
|
27
|
+
data: T | null;
|
|
28
|
+
error: {
|
|
29
|
+
message: string;
|
|
30
|
+
code?: string;
|
|
31
|
+
details?: string;
|
|
32
|
+
} | null;
|
|
33
|
+
count?: number | null;
|
|
34
|
+
}
|