@fluxbase/sdk 0.0.1-rc.30 → 0.0.1-rc.31
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 +165 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -4
- package/dist/index.d.ts +109 -4
- package/dist/index.js +165 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3961,8 +3961,11 @@ var QueryBuilder = class {
|
|
|
3961
3961
|
constructor(fetch2, table) {
|
|
3962
3962
|
this.selectQuery = "*";
|
|
3963
3963
|
this.filters = [];
|
|
3964
|
+
this.orFilters = [];
|
|
3965
|
+
this.andFilters = [];
|
|
3964
3966
|
this.orderBys = [];
|
|
3965
3967
|
this.singleRow = false;
|
|
3968
|
+
this.maybeSingleRow = false;
|
|
3966
3969
|
this.fetch = fetch2;
|
|
3967
3970
|
this.table = table;
|
|
3968
3971
|
}
|
|
@@ -3991,15 +3994,29 @@ var QueryBuilder = class {
|
|
|
3991
3994
|
};
|
|
3992
3995
|
}
|
|
3993
3996
|
/**
|
|
3994
|
-
* Upsert (insert or update) rows
|
|
3997
|
+
* Upsert (insert or update) rows (Supabase-compatible)
|
|
3998
|
+
* @param data - Row(s) to upsert
|
|
3999
|
+
* @param options - Upsert options (onConflict, ignoreDuplicates, defaultToNull)
|
|
3995
4000
|
*/
|
|
3996
|
-
async upsert(data) {
|
|
4001
|
+
async upsert(data, options) {
|
|
3997
4002
|
const body = Array.isArray(data) ? data : data;
|
|
3998
|
-
const
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
+
const preferValues = [];
|
|
4004
|
+
if (options?.ignoreDuplicates) {
|
|
4005
|
+
preferValues.push("resolution=ignore-duplicates");
|
|
4006
|
+
} else {
|
|
4007
|
+
preferValues.push("resolution=merge-duplicates");
|
|
4008
|
+
}
|
|
4009
|
+
if (options?.defaultToNull) {
|
|
4010
|
+
preferValues.push("missing=default");
|
|
4011
|
+
}
|
|
4012
|
+
const headers = {
|
|
4013
|
+
Prefer: preferValues.join(",")
|
|
4014
|
+
};
|
|
4015
|
+
let path = `/api/v1/tables/${this.table}`;
|
|
4016
|
+
if (options?.onConflict) {
|
|
4017
|
+
path += `?on_conflict=${encodeURIComponent(options.onConflict)}`;
|
|
4018
|
+
}
|
|
4019
|
+
const response = await this.fetch.post(path, body, { headers });
|
|
4003
4020
|
return {
|
|
4004
4021
|
data: response,
|
|
4005
4022
|
error: null,
|
|
@@ -4122,6 +4139,71 @@ var QueryBuilder = class {
|
|
|
4122
4139
|
this.filters.push({ column, operator: "fts", value: query });
|
|
4123
4140
|
return this;
|
|
4124
4141
|
}
|
|
4142
|
+
/**
|
|
4143
|
+
* Negate a filter condition (Supabase-compatible)
|
|
4144
|
+
* @example not('status', 'eq', 'deleted')
|
|
4145
|
+
* @example not('completed_at', 'is', null)
|
|
4146
|
+
*/
|
|
4147
|
+
not(column, operator, value) {
|
|
4148
|
+
this.filters.push({ column, operator: "not", value: `${operator}.${this.formatValue(value)}` });
|
|
4149
|
+
return this;
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Apply OR logic to filters (Supabase-compatible)
|
|
4153
|
+
* @example or('status.eq.active,status.eq.pending')
|
|
4154
|
+
* @example or('id.eq.2,name.eq.Han')
|
|
4155
|
+
*/
|
|
4156
|
+
or(filters) {
|
|
4157
|
+
this.orFilters.push(filters);
|
|
4158
|
+
return this;
|
|
4159
|
+
}
|
|
4160
|
+
/**
|
|
4161
|
+
* Apply AND logic to filters (Supabase-compatible)
|
|
4162
|
+
* Groups multiple conditions that must all be true
|
|
4163
|
+
* @example and('status.eq.active,verified.eq.true')
|
|
4164
|
+
* @example and('age.gte.18,age.lte.65')
|
|
4165
|
+
*/
|
|
4166
|
+
and(filters) {
|
|
4167
|
+
this.andFilters.push(filters);
|
|
4168
|
+
return this;
|
|
4169
|
+
}
|
|
4170
|
+
/**
|
|
4171
|
+
* Match multiple columns with exact values (Supabase-compatible)
|
|
4172
|
+
* Shorthand for multiple .eq() calls
|
|
4173
|
+
* @example match({ id: 1, status: 'active', role: 'admin' })
|
|
4174
|
+
*/
|
|
4175
|
+
match(conditions) {
|
|
4176
|
+
for (const [column, value] of Object.entries(conditions)) {
|
|
4177
|
+
this.eq(column, value);
|
|
4178
|
+
}
|
|
4179
|
+
return this;
|
|
4180
|
+
}
|
|
4181
|
+
/**
|
|
4182
|
+
* Generic filter method using PostgREST syntax (Supabase-compatible)
|
|
4183
|
+
* @example filter('name', 'in', '("Han","Yoda")')
|
|
4184
|
+
* @example filter('age', 'gte', '18')
|
|
4185
|
+
*/
|
|
4186
|
+
filter(column, operator, value) {
|
|
4187
|
+
this.filters.push({ column, operator, value });
|
|
4188
|
+
return this;
|
|
4189
|
+
}
|
|
4190
|
+
/**
|
|
4191
|
+
* Check if column is contained by value (Supabase-compatible)
|
|
4192
|
+
* For arrays and JSONB
|
|
4193
|
+
* @example containedBy('tags', '["news","update"]')
|
|
4194
|
+
*/
|
|
4195
|
+
containedBy(column, value) {
|
|
4196
|
+
this.filters.push({ column, operator: "cd", value });
|
|
4197
|
+
return this;
|
|
4198
|
+
}
|
|
4199
|
+
/**
|
|
4200
|
+
* Check if arrays have common elements (Supabase-compatible)
|
|
4201
|
+
* @example overlaps('tags', '["news","sports"]')
|
|
4202
|
+
*/
|
|
4203
|
+
overlaps(column, value) {
|
|
4204
|
+
this.filters.push({ column, operator: "ov", value });
|
|
4205
|
+
return this;
|
|
4206
|
+
}
|
|
4125
4207
|
/**
|
|
4126
4208
|
* Order results
|
|
4127
4209
|
*/
|
|
@@ -4149,12 +4231,32 @@ var QueryBuilder = class {
|
|
|
4149
4231
|
}
|
|
4150
4232
|
/**
|
|
4151
4233
|
* Return a single row (adds limit(1))
|
|
4234
|
+
* Errors if no rows found
|
|
4152
4235
|
*/
|
|
4153
4236
|
single() {
|
|
4154
4237
|
this.singleRow = true;
|
|
4155
4238
|
this.limitValue = 1;
|
|
4156
4239
|
return this;
|
|
4157
4240
|
}
|
|
4241
|
+
/**
|
|
4242
|
+
* Return a single row or null (adds limit(1))
|
|
4243
|
+
* Does not error if no rows found (Supabase-compatible)
|
|
4244
|
+
* @example
|
|
4245
|
+
* ```typescript
|
|
4246
|
+
* // Returns null instead of erroring when no row exists
|
|
4247
|
+
* const { data, error } = await client
|
|
4248
|
+
* .from('users')
|
|
4249
|
+
* .select('*')
|
|
4250
|
+
* .eq('id', 999)
|
|
4251
|
+
* .maybeSingle()
|
|
4252
|
+
* // data will be null if no row found
|
|
4253
|
+
* ```
|
|
4254
|
+
*/
|
|
4255
|
+
maybeSingle() {
|
|
4256
|
+
this.maybeSingleRow = true;
|
|
4257
|
+
this.limitValue = 1;
|
|
4258
|
+
return this;
|
|
4259
|
+
}
|
|
4158
4260
|
/**
|
|
4159
4261
|
* Range selection (pagination)
|
|
4160
4262
|
*/
|
|
@@ -4422,6 +4524,25 @@ var QueryBuilder = class {
|
|
|
4422
4524
|
statusText: "OK"
|
|
4423
4525
|
};
|
|
4424
4526
|
}
|
|
4527
|
+
if (this.maybeSingleRow) {
|
|
4528
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
4529
|
+
return {
|
|
4530
|
+
data: null,
|
|
4531
|
+
error: null,
|
|
4532
|
+
count: 0,
|
|
4533
|
+
status: 200,
|
|
4534
|
+
statusText: "OK"
|
|
4535
|
+
};
|
|
4536
|
+
}
|
|
4537
|
+
const singleData = Array.isArray(data) ? data[0] : data;
|
|
4538
|
+
return {
|
|
4539
|
+
data: singleData,
|
|
4540
|
+
error: null,
|
|
4541
|
+
count: 1,
|
|
4542
|
+
status: 200,
|
|
4543
|
+
statusText: "OK"
|
|
4544
|
+
};
|
|
4545
|
+
}
|
|
4425
4546
|
return {
|
|
4426
4547
|
data,
|
|
4427
4548
|
error: null,
|
|
@@ -4443,6 +4564,37 @@ var QueryBuilder = class {
|
|
|
4443
4564
|
};
|
|
4444
4565
|
}
|
|
4445
4566
|
}
|
|
4567
|
+
/**
|
|
4568
|
+
* Execute the query and throw an error if one occurs (Supabase-compatible)
|
|
4569
|
+
* Returns the data directly instead of { data, error } wrapper
|
|
4570
|
+
*
|
|
4571
|
+
* @throws {Error} If the query fails or returns an error
|
|
4572
|
+
* @example
|
|
4573
|
+
* ```typescript
|
|
4574
|
+
* // Throws error instead of returning { data, error }
|
|
4575
|
+
* try {
|
|
4576
|
+
* const user = await client
|
|
4577
|
+
* .from('users')
|
|
4578
|
+
* .select('*')
|
|
4579
|
+
* .eq('id', 1)
|
|
4580
|
+
* .single()
|
|
4581
|
+
* .throwOnError()
|
|
4582
|
+
* } catch (error) {
|
|
4583
|
+
* console.error('Query failed:', error)
|
|
4584
|
+
* }
|
|
4585
|
+
* ```
|
|
4586
|
+
*/
|
|
4587
|
+
async throwOnError() {
|
|
4588
|
+
const response = await this.execute();
|
|
4589
|
+
if (response.error) {
|
|
4590
|
+
const error = new Error(response.error.message);
|
|
4591
|
+
if (response.error.code) {
|
|
4592
|
+
error.code = response.error.code;
|
|
4593
|
+
}
|
|
4594
|
+
throw error;
|
|
4595
|
+
}
|
|
4596
|
+
return response.data;
|
|
4597
|
+
}
|
|
4446
4598
|
/**
|
|
4447
4599
|
* Make QueryBuilder awaitable (implements PromiseLike)
|
|
4448
4600
|
* This allows using `await client.from('table').select()` without calling `.execute()`
|
|
@@ -4470,6 +4622,12 @@ var QueryBuilder = class {
|
|
|
4470
4622
|
for (const filter of this.filters) {
|
|
4471
4623
|
params.append(filter.column, `${filter.operator}.${this.formatValue(filter.value)}`);
|
|
4472
4624
|
}
|
|
4625
|
+
for (const orFilter of this.orFilters) {
|
|
4626
|
+
params.append("or", `(${orFilter})`);
|
|
4627
|
+
}
|
|
4628
|
+
for (const andFilter of this.andFilters) {
|
|
4629
|
+
params.append("and", `(${andFilter})`);
|
|
4630
|
+
}
|
|
4473
4631
|
if (this.groupByColumns && this.groupByColumns.length > 0) {
|
|
4474
4632
|
params.append("group_by", this.groupByColumns.join(","));
|
|
4475
4633
|
}
|