@fluxbase/sdk 0.0.1-rc.30 → 0.0.1-rc.32
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 +224 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +116 -12
- package/dist/index.d.ts +116 -12
- package/dist/index.js +224 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3959,8 +3959,12 @@ var QueryBuilder = class {
|
|
|
3959
3959
|
constructor(fetch2, table) {
|
|
3960
3960
|
this.selectQuery = "*";
|
|
3961
3961
|
this.filters = [];
|
|
3962
|
+
this.orFilters = [];
|
|
3963
|
+
this.andFilters = [];
|
|
3962
3964
|
this.orderBys = [];
|
|
3963
3965
|
this.singleRow = false;
|
|
3966
|
+
this.maybeSingleRow = false;
|
|
3967
|
+
this.operationType = "select";
|
|
3964
3968
|
this.fetch = fetch2;
|
|
3965
3969
|
this.table = table;
|
|
3966
3970
|
}
|
|
@@ -3977,27 +3981,35 @@ var QueryBuilder = class {
|
|
|
3977
3981
|
/**
|
|
3978
3982
|
* Insert a single row or multiple rows
|
|
3979
3983
|
*/
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
return
|
|
3984
|
-
data: response,
|
|
3985
|
-
error: null,
|
|
3986
|
-
count: Array.isArray(data) ? data.length : 1,
|
|
3987
|
-
status: 201,
|
|
3988
|
-
statusText: "Created"
|
|
3989
|
-
};
|
|
3984
|
+
insert(data) {
|
|
3985
|
+
this.operationType = "insert";
|
|
3986
|
+
this.insertData = data;
|
|
3987
|
+
return this;
|
|
3990
3988
|
}
|
|
3991
3989
|
/**
|
|
3992
|
-
* Upsert (insert or update) rows
|
|
3990
|
+
* Upsert (insert or update) rows (Supabase-compatible)
|
|
3991
|
+
* @param data - Row(s) to upsert
|
|
3992
|
+
* @param options - Upsert options (onConflict, ignoreDuplicates, defaultToNull)
|
|
3993
3993
|
*/
|
|
3994
|
-
async upsert(data) {
|
|
3994
|
+
async upsert(data, options) {
|
|
3995
3995
|
const body = Array.isArray(data) ? data : data;
|
|
3996
|
-
const
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
3996
|
+
const preferValues = [];
|
|
3997
|
+
if (options?.ignoreDuplicates) {
|
|
3998
|
+
preferValues.push("resolution=ignore-duplicates");
|
|
3999
|
+
} else {
|
|
4000
|
+
preferValues.push("resolution=merge-duplicates");
|
|
4001
|
+
}
|
|
4002
|
+
if (options?.defaultToNull) {
|
|
4003
|
+
preferValues.push("missing=default");
|
|
4004
|
+
}
|
|
4005
|
+
const headers = {
|
|
4006
|
+
Prefer: preferValues.join(",")
|
|
4007
|
+
};
|
|
4008
|
+
let path = `/api/v1/tables/${this.table}`;
|
|
4009
|
+
if (options?.onConflict) {
|
|
4010
|
+
path += `?on_conflict=${encodeURIComponent(options.onConflict)}`;
|
|
4011
|
+
}
|
|
4012
|
+
const response = await this.fetch.post(path, body, { headers });
|
|
4001
4013
|
return {
|
|
4002
4014
|
data: response,
|
|
4003
4015
|
error: null,
|
|
@@ -4009,32 +4021,17 @@ var QueryBuilder = class {
|
|
|
4009
4021
|
/**
|
|
4010
4022
|
* Update rows matching the filters
|
|
4011
4023
|
*/
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
return {
|
|
4017
|
-
data: response,
|
|
4018
|
-
error: null,
|
|
4019
|
-
count: null,
|
|
4020
|
-
status: 200,
|
|
4021
|
-
statusText: "OK"
|
|
4022
|
-
};
|
|
4024
|
+
update(data) {
|
|
4025
|
+
this.operationType = "update";
|
|
4026
|
+
this.updateData = data;
|
|
4027
|
+
return this;
|
|
4023
4028
|
}
|
|
4024
4029
|
/**
|
|
4025
4030
|
* Delete rows matching the filters
|
|
4026
4031
|
*/
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
await this.fetch.delete(path);
|
|
4031
|
-
return {
|
|
4032
|
-
data: null,
|
|
4033
|
-
error: null,
|
|
4034
|
-
count: null,
|
|
4035
|
-
status: 204,
|
|
4036
|
-
statusText: "No Content"
|
|
4037
|
-
};
|
|
4032
|
+
delete() {
|
|
4033
|
+
this.operationType = "delete";
|
|
4034
|
+
return this;
|
|
4038
4035
|
}
|
|
4039
4036
|
/**
|
|
4040
4037
|
* Equal to
|
|
@@ -4120,6 +4117,71 @@ var QueryBuilder = class {
|
|
|
4120
4117
|
this.filters.push({ column, operator: "fts", value: query });
|
|
4121
4118
|
return this;
|
|
4122
4119
|
}
|
|
4120
|
+
/**
|
|
4121
|
+
* Negate a filter condition (Supabase-compatible)
|
|
4122
|
+
* @example not('status', 'eq', 'deleted')
|
|
4123
|
+
* @example not('completed_at', 'is', null)
|
|
4124
|
+
*/
|
|
4125
|
+
not(column, operator, value) {
|
|
4126
|
+
this.filters.push({ column, operator: "not", value: `${operator}.${this.formatValue(value)}` });
|
|
4127
|
+
return this;
|
|
4128
|
+
}
|
|
4129
|
+
/**
|
|
4130
|
+
* Apply OR logic to filters (Supabase-compatible)
|
|
4131
|
+
* @example or('status.eq.active,status.eq.pending')
|
|
4132
|
+
* @example or('id.eq.2,name.eq.Han')
|
|
4133
|
+
*/
|
|
4134
|
+
or(filters) {
|
|
4135
|
+
this.orFilters.push(filters);
|
|
4136
|
+
return this;
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Apply AND logic to filters (Supabase-compatible)
|
|
4140
|
+
* Groups multiple conditions that must all be true
|
|
4141
|
+
* @example and('status.eq.active,verified.eq.true')
|
|
4142
|
+
* @example and('age.gte.18,age.lte.65')
|
|
4143
|
+
*/
|
|
4144
|
+
and(filters) {
|
|
4145
|
+
this.andFilters.push(filters);
|
|
4146
|
+
return this;
|
|
4147
|
+
}
|
|
4148
|
+
/**
|
|
4149
|
+
* Match multiple columns with exact values (Supabase-compatible)
|
|
4150
|
+
* Shorthand for multiple .eq() calls
|
|
4151
|
+
* @example match({ id: 1, status: 'active', role: 'admin' })
|
|
4152
|
+
*/
|
|
4153
|
+
match(conditions) {
|
|
4154
|
+
for (const [column, value] of Object.entries(conditions)) {
|
|
4155
|
+
this.eq(column, value);
|
|
4156
|
+
}
|
|
4157
|
+
return this;
|
|
4158
|
+
}
|
|
4159
|
+
/**
|
|
4160
|
+
* Generic filter method using PostgREST syntax (Supabase-compatible)
|
|
4161
|
+
* @example filter('name', 'in', '("Han","Yoda")')
|
|
4162
|
+
* @example filter('age', 'gte', '18')
|
|
4163
|
+
*/
|
|
4164
|
+
filter(column, operator, value) {
|
|
4165
|
+
this.filters.push({ column, operator, value });
|
|
4166
|
+
return this;
|
|
4167
|
+
}
|
|
4168
|
+
/**
|
|
4169
|
+
* Check if column is contained by value (Supabase-compatible)
|
|
4170
|
+
* For arrays and JSONB
|
|
4171
|
+
* @example containedBy('tags', '["news","update"]')
|
|
4172
|
+
*/
|
|
4173
|
+
containedBy(column, value) {
|
|
4174
|
+
this.filters.push({ column, operator: "cd", value });
|
|
4175
|
+
return this;
|
|
4176
|
+
}
|
|
4177
|
+
/**
|
|
4178
|
+
* Check if arrays have common elements (Supabase-compatible)
|
|
4179
|
+
* @example overlaps('tags', '["news","sports"]')
|
|
4180
|
+
*/
|
|
4181
|
+
overlaps(column, value) {
|
|
4182
|
+
this.filters.push({ column, operator: "ov", value });
|
|
4183
|
+
return this;
|
|
4184
|
+
}
|
|
4123
4185
|
/**
|
|
4124
4186
|
* Order results
|
|
4125
4187
|
*/
|
|
@@ -4147,12 +4209,32 @@ var QueryBuilder = class {
|
|
|
4147
4209
|
}
|
|
4148
4210
|
/**
|
|
4149
4211
|
* Return a single row (adds limit(1))
|
|
4212
|
+
* Errors if no rows found
|
|
4150
4213
|
*/
|
|
4151
4214
|
single() {
|
|
4152
4215
|
this.singleRow = true;
|
|
4153
4216
|
this.limitValue = 1;
|
|
4154
4217
|
return this;
|
|
4155
4218
|
}
|
|
4219
|
+
/**
|
|
4220
|
+
* Return a single row or null (adds limit(1))
|
|
4221
|
+
* Does not error if no rows found (Supabase-compatible)
|
|
4222
|
+
* @example
|
|
4223
|
+
* ```typescript
|
|
4224
|
+
* // Returns null instead of erroring when no row exists
|
|
4225
|
+
* const { data, error } = await client
|
|
4226
|
+
* .from('users')
|
|
4227
|
+
* .select('*')
|
|
4228
|
+
* .eq('id', 999)
|
|
4229
|
+
* .maybeSingle()
|
|
4230
|
+
* // data will be null if no row found
|
|
4231
|
+
* ```
|
|
4232
|
+
*/
|
|
4233
|
+
maybeSingle() {
|
|
4234
|
+
this.maybeSingleRow = true;
|
|
4235
|
+
this.limitValue = 1;
|
|
4236
|
+
return this;
|
|
4237
|
+
}
|
|
4156
4238
|
/**
|
|
4157
4239
|
* Range selection (pagination)
|
|
4158
4240
|
*/
|
|
@@ -4328,13 +4410,13 @@ var QueryBuilder = class {
|
|
|
4328
4410
|
* { name: 'Alice', email: 'alice@example.com' },
|
|
4329
4411
|
* { name: 'Bob', email: 'bob@example.com' },
|
|
4330
4412
|
* { name: 'Charlie', email: 'charlie@example.com' }
|
|
4331
|
-
* ])
|
|
4413
|
+
* ])
|
|
4332
4414
|
* ```
|
|
4333
4415
|
*
|
|
4334
4416
|
* @category Batch Operations
|
|
4335
4417
|
*/
|
|
4336
4418
|
async insertMany(rows) {
|
|
4337
|
-
return this.insert(rows);
|
|
4419
|
+
return this.insert(rows).execute();
|
|
4338
4420
|
}
|
|
4339
4421
|
/**
|
|
4340
4422
|
* Update multiple rows matching the filters (batch update)
|
|
@@ -4351,19 +4433,17 @@ var QueryBuilder = class {
|
|
|
4351
4433
|
* const { data } = await client.from('products')
|
|
4352
4434
|
* .eq('category', 'electronics')
|
|
4353
4435
|
* .updateMany({ discount: 10, updated_at: new Date() })
|
|
4354
|
-
* .execute()
|
|
4355
4436
|
*
|
|
4356
4437
|
* // Mark all pending orders as processing
|
|
4357
4438
|
* const { data } = await client.from('orders')
|
|
4358
4439
|
* .eq('status', 'pending')
|
|
4359
4440
|
* .updateMany({ status: 'processing' })
|
|
4360
|
-
* .execute()
|
|
4361
4441
|
* ```
|
|
4362
4442
|
*
|
|
4363
4443
|
* @category Batch Operations
|
|
4364
4444
|
*/
|
|
4365
4445
|
async updateMany(data) {
|
|
4366
|
-
return this.update(data);
|
|
4446
|
+
return this.update(data).execute();
|
|
4367
4447
|
}
|
|
4368
4448
|
/**
|
|
4369
4449
|
* Delete multiple rows matching the filters (batch delete)
|
|
@@ -4379,27 +4459,66 @@ var QueryBuilder = class {
|
|
|
4379
4459
|
* await client.from('users')
|
|
4380
4460
|
* .eq('active', false)
|
|
4381
4461
|
* .deleteMany()
|
|
4382
|
-
* .execute()
|
|
4383
4462
|
*
|
|
4384
4463
|
* // Delete old logs
|
|
4385
4464
|
* await client.from('logs')
|
|
4386
4465
|
* .lt('created_at', '2024-01-01')
|
|
4387
4466
|
* .deleteMany()
|
|
4388
|
-
* .execute()
|
|
4389
4467
|
* ```
|
|
4390
4468
|
*
|
|
4391
4469
|
* @category Batch Operations
|
|
4392
4470
|
*/
|
|
4393
4471
|
async deleteMany() {
|
|
4394
|
-
return this.delete();
|
|
4472
|
+
return this.delete().execute();
|
|
4395
4473
|
}
|
|
4396
4474
|
/**
|
|
4397
4475
|
* Execute the query and return results
|
|
4398
4476
|
*/
|
|
4399
4477
|
async execute() {
|
|
4400
|
-
const queryString = this.buildQueryString();
|
|
4401
|
-
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
4402
4478
|
try {
|
|
4479
|
+
if (this.operationType === "insert") {
|
|
4480
|
+
if (!this.insertData) {
|
|
4481
|
+
throw new Error("Insert data is required for insert operation");
|
|
4482
|
+
}
|
|
4483
|
+
const body = Array.isArray(this.insertData) ? this.insertData : this.insertData;
|
|
4484
|
+
const response = await this.fetch.post(`/api/v1/tables/${this.table}`, body);
|
|
4485
|
+
return {
|
|
4486
|
+
data: response,
|
|
4487
|
+
error: null,
|
|
4488
|
+
count: Array.isArray(this.insertData) ? this.insertData.length : 1,
|
|
4489
|
+
status: 201,
|
|
4490
|
+
statusText: "Created"
|
|
4491
|
+
};
|
|
4492
|
+
}
|
|
4493
|
+
if (this.operationType === "update") {
|
|
4494
|
+
if (!this.updateData) {
|
|
4495
|
+
throw new Error("Update data is required for update operation");
|
|
4496
|
+
}
|
|
4497
|
+
const queryString2 = this.buildQueryString();
|
|
4498
|
+
const path2 = `/api/v1/tables/${this.table}${queryString2}`;
|
|
4499
|
+
const response = await this.fetch.patch(path2, this.updateData);
|
|
4500
|
+
return {
|
|
4501
|
+
data: response,
|
|
4502
|
+
error: null,
|
|
4503
|
+
count: null,
|
|
4504
|
+
status: 200,
|
|
4505
|
+
statusText: "OK"
|
|
4506
|
+
};
|
|
4507
|
+
}
|
|
4508
|
+
if (this.operationType === "delete") {
|
|
4509
|
+
const queryString2 = this.buildQueryString();
|
|
4510
|
+
const path2 = `/api/v1/tables/${this.table}${queryString2}`;
|
|
4511
|
+
await this.fetch.delete(path2);
|
|
4512
|
+
return {
|
|
4513
|
+
data: null,
|
|
4514
|
+
error: null,
|
|
4515
|
+
count: null,
|
|
4516
|
+
status: 204,
|
|
4517
|
+
statusText: "No Content"
|
|
4518
|
+
};
|
|
4519
|
+
}
|
|
4520
|
+
const queryString = this.buildQueryString();
|
|
4521
|
+
const path = `/api/v1/tables/${this.table}${queryString}`;
|
|
4403
4522
|
const data = await this.fetch.get(path);
|
|
4404
4523
|
if (this.singleRow) {
|
|
4405
4524
|
if (Array.isArray(data) && data.length === 0) {
|
|
@@ -4420,6 +4539,25 @@ var QueryBuilder = class {
|
|
|
4420
4539
|
statusText: "OK"
|
|
4421
4540
|
};
|
|
4422
4541
|
}
|
|
4542
|
+
if (this.maybeSingleRow) {
|
|
4543
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
4544
|
+
return {
|
|
4545
|
+
data: null,
|
|
4546
|
+
error: null,
|
|
4547
|
+
count: 0,
|
|
4548
|
+
status: 200,
|
|
4549
|
+
statusText: "OK"
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
const singleData = Array.isArray(data) ? data[0] : data;
|
|
4553
|
+
return {
|
|
4554
|
+
data: singleData,
|
|
4555
|
+
error: null,
|
|
4556
|
+
count: 1,
|
|
4557
|
+
status: 200,
|
|
4558
|
+
statusText: "OK"
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4423
4561
|
return {
|
|
4424
4562
|
data,
|
|
4425
4563
|
error: null,
|
|
@@ -4441,6 +4579,37 @@ var QueryBuilder = class {
|
|
|
4441
4579
|
};
|
|
4442
4580
|
}
|
|
4443
4581
|
}
|
|
4582
|
+
/**
|
|
4583
|
+
* Execute the query and throw an error if one occurs (Supabase-compatible)
|
|
4584
|
+
* Returns the data directly instead of { data, error } wrapper
|
|
4585
|
+
*
|
|
4586
|
+
* @throws {Error} If the query fails or returns an error
|
|
4587
|
+
* @example
|
|
4588
|
+
* ```typescript
|
|
4589
|
+
* // Throws error instead of returning { data, error }
|
|
4590
|
+
* try {
|
|
4591
|
+
* const user = await client
|
|
4592
|
+
* .from('users')
|
|
4593
|
+
* .select('*')
|
|
4594
|
+
* .eq('id', 1)
|
|
4595
|
+
* .single()
|
|
4596
|
+
* .throwOnError()
|
|
4597
|
+
* } catch (error) {
|
|
4598
|
+
* console.error('Query failed:', error)
|
|
4599
|
+
* }
|
|
4600
|
+
* ```
|
|
4601
|
+
*/
|
|
4602
|
+
async throwOnError() {
|
|
4603
|
+
const response = await this.execute();
|
|
4604
|
+
if (response.error) {
|
|
4605
|
+
const error = new Error(response.error.message);
|
|
4606
|
+
if (response.error.code) {
|
|
4607
|
+
error.code = response.error.code;
|
|
4608
|
+
}
|
|
4609
|
+
throw error;
|
|
4610
|
+
}
|
|
4611
|
+
return response.data;
|
|
4612
|
+
}
|
|
4444
4613
|
/**
|
|
4445
4614
|
* Make QueryBuilder awaitable (implements PromiseLike)
|
|
4446
4615
|
* This allows using `await client.from('table').select()` without calling `.execute()`
|
|
@@ -4468,6 +4637,12 @@ var QueryBuilder = class {
|
|
|
4468
4637
|
for (const filter of this.filters) {
|
|
4469
4638
|
params.append(filter.column, `${filter.operator}.${this.formatValue(filter.value)}`);
|
|
4470
4639
|
}
|
|
4640
|
+
for (const orFilter of this.orFilters) {
|
|
4641
|
+
params.append("or", `(${orFilter})`);
|
|
4642
|
+
}
|
|
4643
|
+
for (const andFilter of this.andFilters) {
|
|
4644
|
+
params.append("and", `(${andFilter})`);
|
|
4645
|
+
}
|
|
4471
4646
|
if (this.groupByColumns && this.groupByColumns.length > 0) {
|
|
4472
4647
|
params.append("group_by", this.groupByColumns.join(","));
|
|
4473
4648
|
}
|