@seaverse/dataservice 1.0.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/LICENSE +21 -0
- package/README.md +389 -0
- package/dist/index.d.mts +308 -0
- package/dist/index.d.ts +308 -0
- package/dist/index.js +371 -0
- package/dist/index.mjs +342 -0
- package/package.json +66 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for SeaVerse Data Service SDK
|
|
3
|
+
*
|
|
4
|
+
* These types are designed to be AI-friendly:
|
|
5
|
+
* - Clear, descriptive names
|
|
6
|
+
* - Self-documenting structure
|
|
7
|
+
* - Rich JSDoc comments
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Configuration options for creating a Data Service client
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const config: ClientConfig = {
|
|
15
|
+
* url: 'https://your-postgrest-api.example.com',
|
|
16
|
+
* token: 'Bearer your-jwt-token-here',
|
|
17
|
+
* };
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
interface ClientConfig {
|
|
21
|
+
/** PostgREST API base URL */
|
|
22
|
+
url: string;
|
|
23
|
+
/** JWT token containing user_id in payload.user_id */
|
|
24
|
+
token: string;
|
|
25
|
+
/** Optional configuration */
|
|
26
|
+
options?: {
|
|
27
|
+
/** Custom fetch implementation (useful for Node.js < 18) */
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
/** Additional HTTP headers to include in all requests */
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
32
|
+
timeout?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Data record structure returned from the database
|
|
37
|
+
*
|
|
38
|
+
* @template T - Type of the data field (flexible JSONB)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* type Order = DataRecord<{
|
|
43
|
+
* order_number: string;
|
|
44
|
+
* status: string;
|
|
45
|
+
* total: number;
|
|
46
|
+
* }>;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
interface DataRecord<T = any> {
|
|
50
|
+
/** UUID primary key (database-generated or client-provided) */
|
|
51
|
+
id: string;
|
|
52
|
+
/** User ID (auto-filled from JWT token) */
|
|
53
|
+
user_id: string;
|
|
54
|
+
/** Application identifier */
|
|
55
|
+
app_id: string;
|
|
56
|
+
/** Collection name (like MongoDB collection) */
|
|
57
|
+
collection_name: string;
|
|
58
|
+
/** Flexible JSONB data field */
|
|
59
|
+
data: T;
|
|
60
|
+
/** Creation timestamp (auto-filled) */
|
|
61
|
+
created_at: string;
|
|
62
|
+
/** Last update timestamp (auto-updated) */
|
|
63
|
+
updated_at: string;
|
|
64
|
+
/** Soft delete timestamp (null if not deleted) */
|
|
65
|
+
deleted_at: string | null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Query filter operators for building complex queries
|
|
69
|
+
*
|
|
70
|
+
* AI-friendly: Each operator has a clear, predictable name
|
|
71
|
+
*/
|
|
72
|
+
interface QueryFilter {
|
|
73
|
+
/** Field path (supports JSONB paths like 'data->status') */
|
|
74
|
+
field: string;
|
|
75
|
+
/** Operator type */
|
|
76
|
+
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'contains' | 'overlaps';
|
|
77
|
+
/** Value to compare against */
|
|
78
|
+
value: any;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Query builder options for ordering results
|
|
82
|
+
*/
|
|
83
|
+
interface OrderOptions {
|
|
84
|
+
/** Sort in descending order (default: false) */
|
|
85
|
+
descending?: boolean;
|
|
86
|
+
/** Handle null values (first or last) */
|
|
87
|
+
nullsFirst?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Statistics about user's data
|
|
91
|
+
*
|
|
92
|
+
* Returned by getStats() RPC function
|
|
93
|
+
*/
|
|
94
|
+
interface UserDataStats {
|
|
95
|
+
/** Total number of records (including deleted) */
|
|
96
|
+
total_records: number;
|
|
97
|
+
/** Number of active (non-deleted) records */
|
|
98
|
+
active_records: number;
|
|
99
|
+
/** Number of soft-deleted records */
|
|
100
|
+
deleted_records: number;
|
|
101
|
+
/** Number of unique app_ids */
|
|
102
|
+
total_apps: number;
|
|
103
|
+
/** Number of unique collection_names */
|
|
104
|
+
total_collections: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Error response from PostgREST API
|
|
108
|
+
*/
|
|
109
|
+
interface APIError {
|
|
110
|
+
/** Error code (e.g., '23505' for unique constraint violation) */
|
|
111
|
+
code?: string;
|
|
112
|
+
/** Human-readable error message */
|
|
113
|
+
message: string;
|
|
114
|
+
/** Additional error details */
|
|
115
|
+
details?: string;
|
|
116
|
+
/** Hint for resolving the error */
|
|
117
|
+
hint?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Custom error class for SDK errors
|
|
121
|
+
*
|
|
122
|
+
* AI-friendly: Clear error types and messages
|
|
123
|
+
*/
|
|
124
|
+
declare class DataServiceError extends Error {
|
|
125
|
+
code?: string | undefined;
|
|
126
|
+
details?: string | undefined;
|
|
127
|
+
hint?: string | undefined;
|
|
128
|
+
statusCode?: number | undefined;
|
|
129
|
+
constructor(message: string, code?: string | undefined, details?: string | undefined, hint?: string | undefined, statusCode?: number | undefined);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Query builder interface for fluent API
|
|
133
|
+
*
|
|
134
|
+
* AI-friendly: Method chaining with clear intent
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const results = await query
|
|
139
|
+
* .eq('status', 'active')
|
|
140
|
+
* .gt('total', 100)
|
|
141
|
+
* .order('created_at', { descending: true })
|
|
142
|
+
* .limit(20);
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
interface QueryBuilder<T = any> {
|
|
146
|
+
/** Filter: field equals value */
|
|
147
|
+
eq(field: string, value: any): QueryBuilder<T>;
|
|
148
|
+
/** Filter: field not equals value */
|
|
149
|
+
neq(field: string, value: any): QueryBuilder<T>;
|
|
150
|
+
/** Filter: field greater than value */
|
|
151
|
+
gt(field: string, value: any): QueryBuilder<T>;
|
|
152
|
+
/** Filter: field greater than or equal to value */
|
|
153
|
+
gte(field: string, value: any): QueryBuilder<T>;
|
|
154
|
+
/** Filter: field less than value */
|
|
155
|
+
lt(field: string, value: any): QueryBuilder<T>;
|
|
156
|
+
/** Filter: field less than or equal to value */
|
|
157
|
+
lte(field: string, value: any): QueryBuilder<T>;
|
|
158
|
+
/** Filter: field matches pattern (case-sensitive) */
|
|
159
|
+
like(field: string, pattern: string): QueryBuilder<T>;
|
|
160
|
+
/** Filter: field matches pattern (case-insensitive) */
|
|
161
|
+
ilike(field: string, pattern: string): QueryBuilder<T>;
|
|
162
|
+
/** Filter: field value in array */
|
|
163
|
+
in(field: string, values: any[]): QueryBuilder<T>;
|
|
164
|
+
/** Filter: JSONB contains value */
|
|
165
|
+
contains(value: Record<string, any>): QueryBuilder<T>;
|
|
166
|
+
/** Filter: JSONB overlaps with value */
|
|
167
|
+
overlaps(value: Record<string, any>): QueryBuilder<T>;
|
|
168
|
+
/** Order results by field */
|
|
169
|
+
order(field: string, options?: OrderOptions): QueryBuilder<T>;
|
|
170
|
+
/** Limit number of results */
|
|
171
|
+
limit(count: number): QueryBuilder<T>;
|
|
172
|
+
/** Skip number of results (pagination) */
|
|
173
|
+
offset(count: number): QueryBuilder<T>;
|
|
174
|
+
/** Execute query and return results */
|
|
175
|
+
execute(): Promise<DataRecord<T>[]>;
|
|
176
|
+
/** Execute query and return count */
|
|
177
|
+
count(): Promise<number>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Collection interface for CRUD operations
|
|
181
|
+
*
|
|
182
|
+
* AI-friendly: Clear method names indicating intent
|
|
183
|
+
*/
|
|
184
|
+
interface Collection<T = any> {
|
|
185
|
+
/** Insert a new record (optionally provide UUID) */
|
|
186
|
+
insert(data: T, id?: string): Promise<DataRecord<T>>;
|
|
187
|
+
/** Get a single record by ID (alias for selectById) */
|
|
188
|
+
get(id: string): Promise<DataRecord<T> | null>;
|
|
189
|
+
/** Select records with optional filters */
|
|
190
|
+
select(): QueryBuilder<T>;
|
|
191
|
+
/** Select a single record by ID */
|
|
192
|
+
selectById(id: string): Promise<DataRecord<T> | null>;
|
|
193
|
+
/** Update a record by ID (replaces entire data field) */
|
|
194
|
+
update(id: string, data: T): Promise<DataRecord<T>>;
|
|
195
|
+
/** Patch a record by ID (merges with existing data) */
|
|
196
|
+
patch(id: string, partial: Partial<T>): Promise<DataRecord<T>>;
|
|
197
|
+
/** Hard delete a record by ID */
|
|
198
|
+
delete(id: string): Promise<void>;
|
|
199
|
+
/** Soft delete a record by ID (sets deleted_at) */
|
|
200
|
+
softDelete(id: string): Promise<boolean>;
|
|
201
|
+
/** Restore a soft-deleted record */
|
|
202
|
+
restore(id: string): Promise<boolean>;
|
|
203
|
+
/** Search records by JSONB contains */
|
|
204
|
+
search(criteria: Partial<T>): Promise<DataRecord<T>[]>;
|
|
205
|
+
/** Count records in collection */
|
|
206
|
+
count(): Promise<number>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Data table interface for accessing collections within a specific table
|
|
210
|
+
*
|
|
211
|
+
* Represents a physical database table (e.g., user_data, public_data)
|
|
212
|
+
*/
|
|
213
|
+
interface DataTable {
|
|
214
|
+
/** Get a collection from this table */
|
|
215
|
+
collection<T = any>(name: string): Collection<T>;
|
|
216
|
+
/** Batch insert multiple records into different collections */
|
|
217
|
+
batchInsert<T = any>(baseCollectionName: string, records: T[]): Promise<DataRecord<T>[]>;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Main client interface
|
|
221
|
+
*
|
|
222
|
+
* AI-friendly: Entry point with clear hierarchy
|
|
223
|
+
*/
|
|
224
|
+
interface DataServiceClient {
|
|
225
|
+
/** Automatically extracted application ID from current URL */
|
|
226
|
+
readonly appId: string;
|
|
227
|
+
/** Access user private data table (user_data) */
|
|
228
|
+
readonly userData: DataTable;
|
|
229
|
+
/** Get user data statistics (RPC call) */
|
|
230
|
+
getStats(): Promise<UserDataStats>;
|
|
231
|
+
/** Health check (RPC call) */
|
|
232
|
+
health(): Promise<{
|
|
233
|
+
status: string;
|
|
234
|
+
user_id: string;
|
|
235
|
+
}>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Core client implementation for SeaVerse Data Service SDK
|
|
240
|
+
*
|
|
241
|
+
* Design principles:
|
|
242
|
+
* 1. AI-friendly: Clear method names, predictable behavior
|
|
243
|
+
* 2. Type-safe: Full TypeScript support
|
|
244
|
+
* 3. Chainable: Fluent API for query building
|
|
245
|
+
* 4. Secure: RLS enforced, token-based auth
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Create a new Data Service client
|
|
250
|
+
*
|
|
251
|
+
* AI-friendly: Single entry point with clear configuration
|
|
252
|
+
*
|
|
253
|
+
* @param config - Client configuration with URL and JWT token
|
|
254
|
+
* @returns DataServiceClient instance
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* const client = createClient({
|
|
259
|
+
* url: 'https://your-postgrest-api.example.com',
|
|
260
|
+
* token: 'Bearer your-jwt-token-here',
|
|
261
|
+
* });
|
|
262
|
+
*
|
|
263
|
+
* // appId is automatically extracted from current URL
|
|
264
|
+
* // Direct access to collections - clean and simple
|
|
265
|
+
* const order = await client.userData.collection('orders').insert({ ... });
|
|
266
|
+
* const orders = await client.userData.collection('orders').select().execute();
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
declare function createClient(config: ClientConfig): DataServiceClient;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* SeaVerse Data Service SDK
|
|
273
|
+
*
|
|
274
|
+
* AI-Friendly Universal Data Storage for TypeScript/JavaScript
|
|
275
|
+
*
|
|
276
|
+
* @packageDocumentation
|
|
277
|
+
*
|
|
278
|
+
* @example Basic Usage
|
|
279
|
+
* ```typescript
|
|
280
|
+
* import { createClient } from '@seaverse/data-service-sdk';
|
|
281
|
+
*
|
|
282
|
+
* // appId is automatically extracted from current URL
|
|
283
|
+
* const client = createClient({
|
|
284
|
+
* url: 'https://postgrest.example.com',
|
|
285
|
+
* token: 'your-jwt-token',
|
|
286
|
+
* });
|
|
287
|
+
*
|
|
288
|
+
* // Insert data - appId is automatically included
|
|
289
|
+
* const order = await client.userData.collection('orders').insert({
|
|
290
|
+
* order_number: 'ORD-123',
|
|
291
|
+
* status: 'pending',
|
|
292
|
+
* total: 99.99,
|
|
293
|
+
* });
|
|
294
|
+
*
|
|
295
|
+
* // Query data
|
|
296
|
+
* const orders = await client.userData
|
|
297
|
+
* .collection('orders')
|
|
298
|
+
* .select()
|
|
299
|
+
* .eq('status', 'pending')
|
|
300
|
+
* .order('created_at', { descending: true })
|
|
301
|
+
* .limit(10)
|
|
302
|
+
* .execute();
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
declare const VERSION = "1.0.0";
|
|
307
|
+
|
|
308
|
+
export { type APIError, type ClientConfig, type Collection, type DataRecord, type DataServiceClient, DataServiceError, type DataTable, type OrderOptions, type QueryBuilder, type QueryFilter, type UserDataStats, VERSION, createClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
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 src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
DataServiceError: () => DataServiceError,
|
|
24
|
+
VERSION: () => VERSION,
|
|
25
|
+
createClient: () => createClient
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
|
+
|
|
29
|
+
// src/types.ts
|
|
30
|
+
var DataServiceError = class extends Error {
|
|
31
|
+
constructor(message, code, details, hint, statusCode) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.details = details;
|
|
35
|
+
this.hint = hint;
|
|
36
|
+
this.statusCode = statusCode;
|
|
37
|
+
this.name = "DataServiceError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/client.ts
|
|
42
|
+
function extractAppId() {
|
|
43
|
+
if (typeof globalThis !== "undefined" && "location" in globalThis) {
|
|
44
|
+
const location = globalThis.location;
|
|
45
|
+
const hostname = location.hostname;
|
|
46
|
+
const withoutSeaverse = hostname.replace(/\.app\.seaverse\.ai$/, "").replace(/\.seaverse\.ai$/, "");
|
|
47
|
+
return withoutSeaverse !== hostname ? withoutSeaverse : hostname;
|
|
48
|
+
}
|
|
49
|
+
if (typeof process !== "undefined" && process.env) {
|
|
50
|
+
return process.env.SEAVERSE_APP_ID || "default";
|
|
51
|
+
}
|
|
52
|
+
return "default";
|
|
53
|
+
}
|
|
54
|
+
var HTTPClient = class {
|
|
55
|
+
baseUrl;
|
|
56
|
+
headers;
|
|
57
|
+
fetchFn;
|
|
58
|
+
timeout;
|
|
59
|
+
constructor(config) {
|
|
60
|
+
this.baseUrl = config.url.replace(/\/$/, "");
|
|
61
|
+
this.fetchFn = config.options?.fetch || globalThis.fetch;
|
|
62
|
+
this.timeout = config.options?.timeout || 3e4;
|
|
63
|
+
this.headers = {
|
|
64
|
+
"Authorization": config.token,
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
"Prefer": "return=representation",
|
|
67
|
+
...config.options?.headers
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Make HTTP request with timeout and error handling
|
|
72
|
+
*/
|
|
73
|
+
async request(method, path, body, additionalHeaders) {
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
78
|
+
method,
|
|
79
|
+
headers: { ...this.headers, ...additionalHeaders },
|
|
80
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
81
|
+
signal: controller.signal
|
|
82
|
+
});
|
|
83
|
+
clearTimeout(timeoutId);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
let error;
|
|
86
|
+
try {
|
|
87
|
+
error = await response.json();
|
|
88
|
+
} catch {
|
|
89
|
+
error = { message: response.statusText };
|
|
90
|
+
}
|
|
91
|
+
throw new DataServiceError(
|
|
92
|
+
error.message || "Request failed",
|
|
93
|
+
error.code,
|
|
94
|
+
error.details,
|
|
95
|
+
error.hint,
|
|
96
|
+
response.status
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const text = await response.text();
|
|
100
|
+
return text ? JSON.parse(text) : null;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
clearTimeout(timeoutId);
|
|
103
|
+
if (error instanceof DataServiceError) {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
if (error.name === "AbortError") {
|
|
107
|
+
throw new DataServiceError("Request timeout", "TIMEOUT");
|
|
108
|
+
}
|
|
109
|
+
throw new DataServiceError(
|
|
110
|
+
error.message || "Unknown error",
|
|
111
|
+
"NETWORK_ERROR"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async get(path, params) {
|
|
116
|
+
const query = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
117
|
+
return this.request("GET", `${path}${query}`);
|
|
118
|
+
}
|
|
119
|
+
async post(path, body, headers) {
|
|
120
|
+
return this.request("POST", path, body, headers);
|
|
121
|
+
}
|
|
122
|
+
async patch(path, body) {
|
|
123
|
+
return this.request("PATCH", path, body);
|
|
124
|
+
}
|
|
125
|
+
async delete(path) {
|
|
126
|
+
await this.request("DELETE", path);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var QueryBuilderImpl = class {
|
|
130
|
+
constructor(client, tablePath, appId, collectionName) {
|
|
131
|
+
this.client = client;
|
|
132
|
+
this.tablePath = tablePath;
|
|
133
|
+
this.appId = appId;
|
|
134
|
+
this.collectionName = collectionName;
|
|
135
|
+
}
|
|
136
|
+
filters = [];
|
|
137
|
+
ordering = null;
|
|
138
|
+
limitValue = null;
|
|
139
|
+
offsetValue = null;
|
|
140
|
+
eq(field, value) {
|
|
141
|
+
this.filters.push(`${field}=eq.${encodeURIComponent(String(value))}`);
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
neq(field, value) {
|
|
145
|
+
this.filters.push(`${field}=neq.${encodeURIComponent(String(value))}`);
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
gt(field, value) {
|
|
149
|
+
this.filters.push(`${field}=gt.${encodeURIComponent(String(value))}`);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
gte(field, value) {
|
|
153
|
+
this.filters.push(`${field}=gte.${encodeURIComponent(String(value))}`);
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
lt(field, value) {
|
|
157
|
+
this.filters.push(`${field}=lt.${encodeURIComponent(String(value))}`);
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
lte(field, value) {
|
|
161
|
+
this.filters.push(`${field}=lte.${encodeURIComponent(String(value))}`);
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
like(field, pattern) {
|
|
165
|
+
this.filters.push(`${field}=like.${encodeURIComponent(pattern)}`);
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
ilike(field, pattern) {
|
|
169
|
+
this.filters.push(`${field}=ilike.${encodeURIComponent(pattern)}`);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
in(field, values) {
|
|
173
|
+
const encoded = values.map((v) => encodeURIComponent(String(v))).join(",");
|
|
174
|
+
this.filters.push(`${field}=in.(${encoded})`);
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
contains(value) {
|
|
178
|
+
this.filters.push(`data=@>.${encodeURIComponent(JSON.stringify(value))}`);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
overlaps(value) {
|
|
182
|
+
this.filters.push(`data=&&.${encodeURIComponent(JSON.stringify(value))}`);
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
order(field, options = {}) {
|
|
186
|
+
let orderStr = field;
|
|
187
|
+
if (options.descending)
|
|
188
|
+
orderStr += ".desc";
|
|
189
|
+
if (options.nullsFirst !== void 0) {
|
|
190
|
+
orderStr += options.nullsFirst ? ".nullsfirst" : ".nullslast";
|
|
191
|
+
}
|
|
192
|
+
this.ordering = orderStr;
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
limit(count) {
|
|
196
|
+
this.limitValue = count;
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
offset(count) {
|
|
200
|
+
this.offsetValue = count;
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
async execute() {
|
|
204
|
+
const params = {
|
|
205
|
+
app_id: `eq.${this.appId}`,
|
|
206
|
+
collection_name: `eq.${this.collectionName}`,
|
|
207
|
+
...Object.fromEntries(this.filters.map((f, i) => [`filter${i}`, f]))
|
|
208
|
+
};
|
|
209
|
+
if (this.ordering)
|
|
210
|
+
params.order = this.ordering;
|
|
211
|
+
if (this.limitValue !== null)
|
|
212
|
+
params.limit = String(this.limitValue);
|
|
213
|
+
if (this.offsetValue !== null)
|
|
214
|
+
params.offset = String(this.offsetValue);
|
|
215
|
+
return this.client.get(this.tablePath, params);
|
|
216
|
+
}
|
|
217
|
+
async count() {
|
|
218
|
+
const params = {
|
|
219
|
+
app_id: `eq.${this.appId}`,
|
|
220
|
+
collection_name: `eq.${this.collectionName}`,
|
|
221
|
+
...Object.fromEntries(this.filters.map((f, i) => [`filter${i}`, f]))
|
|
222
|
+
};
|
|
223
|
+
const response = await this.client.get(this.tablePath, {
|
|
224
|
+
...params,
|
|
225
|
+
select: "count"
|
|
226
|
+
});
|
|
227
|
+
return response.length;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var CollectionImpl = class {
|
|
231
|
+
constructor(client, tablePath, appId, collectionName) {
|
|
232
|
+
this.client = client;
|
|
233
|
+
this.tablePath = tablePath;
|
|
234
|
+
this.appId = appId;
|
|
235
|
+
this.collectionName = collectionName;
|
|
236
|
+
}
|
|
237
|
+
async insert(data, id) {
|
|
238
|
+
const payload = {
|
|
239
|
+
app_id: this.appId,
|
|
240
|
+
collection_name: this.collectionName,
|
|
241
|
+
data
|
|
242
|
+
};
|
|
243
|
+
if (id) {
|
|
244
|
+
payload.id = id;
|
|
245
|
+
}
|
|
246
|
+
const response = await this.client.post(this.tablePath, payload);
|
|
247
|
+
return Array.isArray(response) ? response[0] : response;
|
|
248
|
+
}
|
|
249
|
+
async get(id) {
|
|
250
|
+
return this.selectById(id);
|
|
251
|
+
}
|
|
252
|
+
select() {
|
|
253
|
+
return new QueryBuilderImpl(this.client, this.tablePath, this.appId, this.collectionName);
|
|
254
|
+
}
|
|
255
|
+
async selectById(id) {
|
|
256
|
+
try {
|
|
257
|
+
const results = await this.client.get(
|
|
258
|
+
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
259
|
+
);
|
|
260
|
+
return results[0] || null;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (error.statusCode === 404) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async update(id, data) {
|
|
269
|
+
const response = await this.client.patch(
|
|
270
|
+
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`,
|
|
271
|
+
{ data }
|
|
272
|
+
);
|
|
273
|
+
return Array.isArray(response) ? response[0] : response;
|
|
274
|
+
}
|
|
275
|
+
async patch(id, partial) {
|
|
276
|
+
const existing = await this.selectById(id);
|
|
277
|
+
if (!existing) {
|
|
278
|
+
throw new DataServiceError(`Record with id ${id} not found`, "NOT_FOUND");
|
|
279
|
+
}
|
|
280
|
+
const merged = { ...existing.data, ...partial };
|
|
281
|
+
return this.update(id, merged);
|
|
282
|
+
}
|
|
283
|
+
async delete(id) {
|
|
284
|
+
await this.client.delete(
|
|
285
|
+
`/user_data?id=eq.${id}&app_id=eq.${this.appId}&collection_name=eq.${this.collectionName}`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
async softDelete(id) {
|
|
289
|
+
const response = await this.client.post("/rpc/soft_delete_user_data", {
|
|
290
|
+
p_id: id
|
|
291
|
+
});
|
|
292
|
+
return response;
|
|
293
|
+
}
|
|
294
|
+
async restore(id) {
|
|
295
|
+
const response = await this.client.post("/rpc/restore_user_data", {
|
|
296
|
+
p_id: id
|
|
297
|
+
});
|
|
298
|
+
return response;
|
|
299
|
+
}
|
|
300
|
+
async search(criteria) {
|
|
301
|
+
return this.client.get(this.tablePath, {
|
|
302
|
+
app_id: `eq.${this.appId}`,
|
|
303
|
+
collection_name: `eq.${this.collectionName}`,
|
|
304
|
+
data: `@>.${JSON.stringify(criteria)}`
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
async count() {
|
|
308
|
+
const results = await this.client.get(this.tablePath, {
|
|
309
|
+
app_id: `eq.${this.appId}`,
|
|
310
|
+
collection_name: `eq.${this.collectionName}`
|
|
311
|
+
});
|
|
312
|
+
return results.length;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
var DataTableImpl = class {
|
|
316
|
+
constructor(client, tablePath, appId) {
|
|
317
|
+
this.client = client;
|
|
318
|
+
this.tablePath = tablePath;
|
|
319
|
+
this.appId = appId;
|
|
320
|
+
}
|
|
321
|
+
collection(name) {
|
|
322
|
+
return new CollectionImpl(this.client, this.tablePath, this.appId, name);
|
|
323
|
+
}
|
|
324
|
+
async batchInsert(baseCollectionName, records) {
|
|
325
|
+
const results = [];
|
|
326
|
+
for (let i = 0; i < records.length; i++) {
|
|
327
|
+
const uniqueCollectionName = `${baseCollectionName}_${Date.now()}_${i}`;
|
|
328
|
+
const collection = new CollectionImpl(
|
|
329
|
+
this.client,
|
|
330
|
+
this.tablePath,
|
|
331
|
+
this.appId,
|
|
332
|
+
uniqueCollectionName
|
|
333
|
+
);
|
|
334
|
+
const result = await collection.insert(records[i]);
|
|
335
|
+
results.push(result);
|
|
336
|
+
if (i < records.length - 1) {
|
|
337
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return results;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
var DataServiceClientImpl = class {
|
|
344
|
+
client;
|
|
345
|
+
userData;
|
|
346
|
+
appId;
|
|
347
|
+
constructor(config) {
|
|
348
|
+
this.client = new HTTPClient(config);
|
|
349
|
+
this.appId = extractAppId();
|
|
350
|
+
this.userData = new DataTableImpl(this.client, "/user_data", this.appId);
|
|
351
|
+
}
|
|
352
|
+
async getStats() {
|
|
353
|
+
const response = await this.client.post("/rpc/get_user_data_stats");
|
|
354
|
+
return Array.isArray(response) ? response[0] : response;
|
|
355
|
+
}
|
|
356
|
+
async health() {
|
|
357
|
+
return this.client.post("/rpc/health");
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
function createClient(config) {
|
|
361
|
+
return new DataServiceClientImpl(config);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/index.ts
|
|
365
|
+
var VERSION = "1.0.0";
|
|
366
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
367
|
+
0 && (module.exports = {
|
|
368
|
+
DataServiceError,
|
|
369
|
+
VERSION,
|
|
370
|
+
createClient
|
|
371
|
+
});
|