@luxdb/sdk 2.0.0 → 2.1.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/README.md +21 -2
- package/dist/cjs/project.js +45 -13
- package/dist/esm/project.js +45 -13
- package/dist/types/project.d.ts +14 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,17 +80,32 @@ const { data: matches } = await lux
|
|
|
80
80
|
.near("embedding", queryEmbedding, { k: 10, threshold: 0.8 });
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
Writes return the affected row(s), including server-generated columns (`id`,
|
|
84
|
+
UUIDv7 primary keys, `DEFAULT now()` timestamps):
|
|
85
|
+
|
|
83
86
|
```ts
|
|
87
|
+
// insert -> the inserted row
|
|
84
88
|
const { data: inserted, error: insertError } = await lux
|
|
85
89
|
.table("messages")
|
|
86
90
|
.insert({ body: "hello", channel: "general" });
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
// bulk insert in a single request -> array of rows
|
|
93
|
+
const { data: many } = await lux
|
|
94
|
+
.table("messages")
|
|
95
|
+
.insert([{ body: "a" }, { body: "b" }]);
|
|
96
|
+
|
|
97
|
+
// upsert: insert, or update the row that conflicts on `onConflict` (default: PK)
|
|
98
|
+
const { data: user } = await lux
|
|
99
|
+
.table("users")
|
|
100
|
+
.upsert({ email: "a@x.com", name: "Bob" }, { onConflict: "email" });
|
|
101
|
+
|
|
102
|
+
// update / delete -> the affected rows
|
|
103
|
+
const { data: updated } = await lux
|
|
89
104
|
.table("messages")
|
|
90
105
|
.update({ body: "edited" })
|
|
91
106
|
.eq("id", inserted?.id);
|
|
92
107
|
|
|
93
|
-
const { data: deleted
|
|
108
|
+
const { data: deleted } = await lux
|
|
94
109
|
.table("messages")
|
|
95
110
|
.delete()
|
|
96
111
|
.eq("id", inserted?.id);
|
|
@@ -116,6 +131,10 @@ await lux.table("events").select().eq("metadata.plan.tier", "pro");
|
|
|
116
131
|
await lux.table("events").select().isValid("metadata.count");
|
|
117
132
|
await lux.table("events").select().isNotValid("metadata.deleted_at");
|
|
118
133
|
|
|
134
|
+
// IS NULL / IS NOT NULL on a regular column (NULL == the column is absent)
|
|
135
|
+
await lux.table("tasks").select().isNull("deleted_at");
|
|
136
|
+
await lux.table("tasks").select().isNotNull("archived_at");
|
|
137
|
+
|
|
119
138
|
// Array membership, and a declared JSON-path index for range queries at scale.
|
|
120
139
|
await lux.table("events").select().contains("tags", "urgent");
|
|
121
140
|
await lux.table("events").createIndex("metadata.plan.tier", "str");
|
package/dist/cjs/project.js
CHANGED
|
@@ -218,6 +218,12 @@ class LuxProjectTable {
|
|
|
218
218
|
insert(rowOrRows) {
|
|
219
219
|
return new LuxProjectInsertBuilder(this.client, this.name, rowOrRows);
|
|
220
220
|
}
|
|
221
|
+
upsert(rowOrRows, options) {
|
|
222
|
+
return new LuxProjectInsertBuilder(this.client, this.name, rowOrRows, {
|
|
223
|
+
upsert: true,
|
|
224
|
+
onConflict: options?.onConflict,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
221
227
|
update(patch) {
|
|
222
228
|
return new LuxProjectMutationBuilder(this.client, this.name, 'PATCH', patch);
|
|
223
229
|
}
|
|
@@ -272,8 +278,17 @@ class LuxProjectFilterBuilder extends LuxProjectThenable {
|
|
|
272
278
|
return this.addFilter(column, 'lte', value);
|
|
273
279
|
}
|
|
274
280
|
is(column, value) {
|
|
281
|
+
// `.is(col, null)` is the Supabase-style spelling of an IS NULL check.
|
|
282
|
+
if (value === null)
|
|
283
|
+
return this.addFilter(column, 'isNull', '');
|
|
275
284
|
return this.addFilter(column, 'is', value);
|
|
276
285
|
}
|
|
286
|
+
isNull(column) {
|
|
287
|
+
return this.addFilter(column, 'isNull', '');
|
|
288
|
+
}
|
|
289
|
+
isNotNull(column) {
|
|
290
|
+
return this.addFilter(column, 'isNotNull', '');
|
|
291
|
+
}
|
|
277
292
|
in(column, values) {
|
|
278
293
|
return this.addFilter(column, 'in', values);
|
|
279
294
|
}
|
|
@@ -589,24 +604,30 @@ class LuxProjectLiveSubscription {
|
|
|
589
604
|
}
|
|
590
605
|
exports.LuxProjectLiveSubscription = LuxProjectLiveSubscription;
|
|
591
606
|
class LuxProjectInsertBuilder extends LuxProjectThenable {
|
|
592
|
-
constructor(client, tableName, rowOrRows) {
|
|
607
|
+
constructor(client, tableName, rowOrRows, upsertOptions) {
|
|
593
608
|
super();
|
|
594
609
|
this.client = client;
|
|
595
610
|
this.tableName = tableName;
|
|
596
611
|
this.rowOrRows = rowOrRows;
|
|
612
|
+
this.upsertOptions = upsertOptions;
|
|
597
613
|
}
|
|
598
614
|
async execute() {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
615
|
+
// One request for both shapes: an array body inserts all rows server-side
|
|
616
|
+
// in a single round-trip. The server returns the affected row(s)
|
|
617
|
+
// ({result: row} for a single row, {result: [rows]} for an array).
|
|
618
|
+
let path = `/tables/${encodeURIComponent(this.tableName)}`;
|
|
619
|
+
if (this.upsertOptions?.upsert) {
|
|
620
|
+
const params = new URLSearchParams();
|
|
621
|
+
if (this.upsertOptions.onConflict)
|
|
622
|
+
params.set('on_conflict', this.upsertOptions.onConflict);
|
|
623
|
+
else
|
|
624
|
+
params.set('upsert', 'true');
|
|
625
|
+
path += `?${params.toString()}`;
|
|
608
626
|
}
|
|
609
|
-
|
|
627
|
+
const res = await this.client.request('POST', path, this.rowOrRows);
|
|
628
|
+
if (res.error)
|
|
629
|
+
return res;
|
|
630
|
+
return (0, utils_1.ok)(unwrapResult(res.data));
|
|
610
631
|
}
|
|
611
632
|
}
|
|
612
633
|
exports.LuxProjectInsertBuilder = LuxProjectInsertBuilder;
|
|
@@ -622,7 +643,11 @@ class LuxProjectMutationBuilder extends LuxProjectFilterBuilder {
|
|
|
622
643
|
}
|
|
623
644
|
const params = this.filteredQueryParams();
|
|
624
645
|
const query = params.toString();
|
|
625
|
-
return
|
|
646
|
+
// Update/delete return the affected rows ({result: [rows]}); unwrap them.
|
|
647
|
+
const res = await this.client.request(this.method, `/tables/${encodeURIComponent(this.tableName)}${query ? `?${query}` : ''}`, this.body);
|
|
648
|
+
if (res.error)
|
|
649
|
+
return res;
|
|
650
|
+
return (0, utils_1.ok)(unwrapResult(res.data));
|
|
626
651
|
}
|
|
627
652
|
}
|
|
628
653
|
exports.LuxProjectMutationBuilder = LuxProjectMutationBuilder;
|
|
@@ -650,7 +675,10 @@ function filtersToWhere(filters) {
|
|
|
650
675
|
const values = Array.isArray(filter.value) ? filter.value : [filter.value];
|
|
651
676
|
return normalizeWhere(`${filter.column} ${op} ( ${values.map(formatWhereValue).join(' ')} )`);
|
|
652
677
|
}
|
|
653
|
-
if (filter.operator === 'isValid' ||
|
|
678
|
+
if (filter.operator === 'isValid' ||
|
|
679
|
+
filter.operator === 'isNotValid' ||
|
|
680
|
+
filter.operator === 'isNull' ||
|
|
681
|
+
filter.operator === 'isNotNull') {
|
|
654
682
|
return normalizeWhere(`${filter.column} ${op}`);
|
|
655
683
|
}
|
|
656
684
|
return normalizeWhere(`${filter.column} ${op} ${formatWhereValue(filter.value)}`);
|
|
@@ -685,6 +713,10 @@ function filterOperatorToWhere(operator) {
|
|
|
685
713
|
return 'IS VALID';
|
|
686
714
|
case 'isNotValid':
|
|
687
715
|
return 'IS NOT VALID';
|
|
716
|
+
case 'isNull':
|
|
717
|
+
return 'IS NULL';
|
|
718
|
+
case 'isNotNull':
|
|
719
|
+
return 'IS NOT NULL';
|
|
688
720
|
case 'contains':
|
|
689
721
|
return 'CONTAINS';
|
|
690
722
|
}
|
package/dist/esm/project.js
CHANGED
|
@@ -212,6 +212,12 @@ export class LuxProjectTable {
|
|
|
212
212
|
insert(rowOrRows) {
|
|
213
213
|
return new LuxProjectInsertBuilder(this.client, this.name, rowOrRows);
|
|
214
214
|
}
|
|
215
|
+
upsert(rowOrRows, options) {
|
|
216
|
+
return new LuxProjectInsertBuilder(this.client, this.name, rowOrRows, {
|
|
217
|
+
upsert: true,
|
|
218
|
+
onConflict: options?.onConflict,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
215
221
|
update(patch) {
|
|
216
222
|
return new LuxProjectMutationBuilder(this.client, this.name, 'PATCH', patch);
|
|
217
223
|
}
|
|
@@ -265,8 +271,17 @@ class LuxProjectFilterBuilder extends LuxProjectThenable {
|
|
|
265
271
|
return this.addFilter(column, 'lte', value);
|
|
266
272
|
}
|
|
267
273
|
is(column, value) {
|
|
274
|
+
// `.is(col, null)` is the Supabase-style spelling of an IS NULL check.
|
|
275
|
+
if (value === null)
|
|
276
|
+
return this.addFilter(column, 'isNull', '');
|
|
268
277
|
return this.addFilter(column, 'is', value);
|
|
269
278
|
}
|
|
279
|
+
isNull(column) {
|
|
280
|
+
return this.addFilter(column, 'isNull', '');
|
|
281
|
+
}
|
|
282
|
+
isNotNull(column) {
|
|
283
|
+
return this.addFilter(column, 'isNotNull', '');
|
|
284
|
+
}
|
|
270
285
|
in(column, values) {
|
|
271
286
|
return this.addFilter(column, 'in', values);
|
|
272
287
|
}
|
|
@@ -580,24 +595,30 @@ export class LuxProjectLiveSubscription {
|
|
|
580
595
|
}
|
|
581
596
|
}
|
|
582
597
|
export class LuxProjectInsertBuilder extends LuxProjectThenable {
|
|
583
|
-
constructor(client, tableName, rowOrRows) {
|
|
598
|
+
constructor(client, tableName, rowOrRows, upsertOptions) {
|
|
584
599
|
super();
|
|
585
600
|
this.client = client;
|
|
586
601
|
this.tableName = tableName;
|
|
587
602
|
this.rowOrRows = rowOrRows;
|
|
603
|
+
this.upsertOptions = upsertOptions;
|
|
588
604
|
}
|
|
589
605
|
async execute() {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const
|
|
596
|
-
if (
|
|
597
|
-
|
|
598
|
-
|
|
606
|
+
// One request for both shapes: an array body inserts all rows server-side
|
|
607
|
+
// in a single round-trip. The server returns the affected row(s)
|
|
608
|
+
// ({result: row} for a single row, {result: [rows]} for an array).
|
|
609
|
+
let path = `/tables/${encodeURIComponent(this.tableName)}`;
|
|
610
|
+
if (this.upsertOptions?.upsert) {
|
|
611
|
+
const params = new URLSearchParams();
|
|
612
|
+
if (this.upsertOptions.onConflict)
|
|
613
|
+
params.set('on_conflict', this.upsertOptions.onConflict);
|
|
614
|
+
else
|
|
615
|
+
params.set('upsert', 'true');
|
|
616
|
+
path += `?${params.toString()}`;
|
|
599
617
|
}
|
|
600
|
-
|
|
618
|
+
const res = await this.client.request('POST', path, this.rowOrRows);
|
|
619
|
+
if (res.error)
|
|
620
|
+
return res;
|
|
621
|
+
return ok(unwrapResult(res.data));
|
|
601
622
|
}
|
|
602
623
|
}
|
|
603
624
|
export class LuxProjectMutationBuilder extends LuxProjectFilterBuilder {
|
|
@@ -612,7 +633,11 @@ export class LuxProjectMutationBuilder extends LuxProjectFilterBuilder {
|
|
|
612
633
|
}
|
|
613
634
|
const params = this.filteredQueryParams();
|
|
614
635
|
const query = params.toString();
|
|
615
|
-
return
|
|
636
|
+
// Update/delete return the affected rows ({result: [rows]}); unwrap them.
|
|
637
|
+
const res = await this.client.request(this.method, `/tables/${encodeURIComponent(this.tableName)}${query ? `?${query}` : ''}`, this.body);
|
|
638
|
+
if (res.error)
|
|
639
|
+
return res;
|
|
640
|
+
return ok(unwrapResult(res.data));
|
|
616
641
|
}
|
|
617
642
|
}
|
|
618
643
|
function unwrapRows(payload) {
|
|
@@ -639,7 +664,10 @@ function filtersToWhere(filters) {
|
|
|
639
664
|
const values = Array.isArray(filter.value) ? filter.value : [filter.value];
|
|
640
665
|
return normalizeWhere(`${filter.column} ${op} ( ${values.map(formatWhereValue).join(' ')} )`);
|
|
641
666
|
}
|
|
642
|
-
if (filter.operator === 'isValid' ||
|
|
667
|
+
if (filter.operator === 'isValid' ||
|
|
668
|
+
filter.operator === 'isNotValid' ||
|
|
669
|
+
filter.operator === 'isNull' ||
|
|
670
|
+
filter.operator === 'isNotNull') {
|
|
643
671
|
return normalizeWhere(`${filter.column} ${op}`);
|
|
644
672
|
}
|
|
645
673
|
return normalizeWhere(`${filter.column} ${op} ${formatWhereValue(filter.value)}`);
|
|
@@ -674,6 +702,10 @@ function filterOperatorToWhere(operator) {
|
|
|
674
702
|
return 'IS VALID';
|
|
675
703
|
case 'isNotValid':
|
|
676
704
|
return 'IS NOT VALID';
|
|
705
|
+
case 'isNull':
|
|
706
|
+
return 'IS NULL';
|
|
707
|
+
case 'isNotNull':
|
|
708
|
+
return 'IS NOT NULL';
|
|
677
709
|
case 'contains':
|
|
678
710
|
return 'CONTAINS';
|
|
679
711
|
}
|
package/dist/types/project.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface LuxVectorSearchOptions {
|
|
|
23
23
|
filter_value?: string;
|
|
24
24
|
}
|
|
25
25
|
type QueryValue = string | number | boolean | number[] | null;
|
|
26
|
-
type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'is' | 'in' | 'notIn' | 'isValid' | 'isNotValid' | 'contains';
|
|
26
|
+
type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'is' | 'in' | 'notIn' | 'isValid' | 'isNotValid' | 'isNull' | 'isNotNull' | 'contains';
|
|
27
27
|
type ProjectRowInput<T extends object> = Partial<T> & Record<string, QueryValue>;
|
|
28
28
|
type ProjectSelectSingle<TResult> = TResult extends readonly (infer Row)[] ? Row : TResult;
|
|
29
29
|
interface QueryFilter {
|
|
@@ -131,6 +131,12 @@ export declare class LuxProjectTable<T extends object> {
|
|
|
131
131
|
live(): Promise<LuxLiveResult<T>>;
|
|
132
132
|
insert(row: ProjectRowInput<T>): LuxProjectInsertBuilder<unknown>;
|
|
133
133
|
insert(rows: Array<ProjectRowInput<T>>): LuxProjectInsertBuilder<unknown[]>;
|
|
134
|
+
upsert(row: ProjectRowInput<T>, options?: {
|
|
135
|
+
onConflict?: string;
|
|
136
|
+
}): LuxProjectInsertBuilder<unknown>;
|
|
137
|
+
upsert(rows: Array<ProjectRowInput<T>>, options?: {
|
|
138
|
+
onConflict?: string;
|
|
139
|
+
}): LuxProjectInsertBuilder<unknown[]>;
|
|
134
140
|
update(patch: ProjectRowInput<T>): LuxProjectMutationBuilder<unknown>;
|
|
135
141
|
delete(): LuxProjectMutationBuilder<unknown>;
|
|
136
142
|
count(): Promise<LuxResult<number>>;
|
|
@@ -160,6 +166,8 @@ declare abstract class LuxProjectFilterBuilder<TResult, TSelf> extends LuxProjec
|
|
|
160
166
|
lt(column: string, value: QueryValue): TSelf;
|
|
161
167
|
lte(column: string, value: QueryValue): TSelf;
|
|
162
168
|
is(column: string, value: QueryValue): TSelf;
|
|
169
|
+
isNull(column: string): TSelf;
|
|
170
|
+
isNotNull(column: string): TSelf;
|
|
163
171
|
in(column: string, values: QueryValue[]): TSelf;
|
|
164
172
|
notIn(column: string, values: QueryValue[]): TSelf;
|
|
165
173
|
isValid(column: string): TSelf;
|
|
@@ -225,7 +233,11 @@ export declare class LuxProjectInsertBuilder<TResult> extends LuxProjectThenable
|
|
|
225
233
|
private client;
|
|
226
234
|
private tableName;
|
|
227
235
|
private rowOrRows;
|
|
228
|
-
|
|
236
|
+
private upsertOptions?;
|
|
237
|
+
constructor(client: LuxProjectClient, tableName: string, rowOrRows: Record<string, QueryValue> | Array<Record<string, QueryValue>>, upsertOptions?: {
|
|
238
|
+
upsert: boolean;
|
|
239
|
+
onConflict?: string;
|
|
240
|
+
} | undefined);
|
|
229
241
|
execute(): Promise<LuxResult<TResult>>;
|
|
230
242
|
}
|
|
231
243
|
export declare class LuxProjectMutationBuilder<TResult> extends LuxProjectFilterBuilder<TResult, LuxProjectMutationBuilder<TResult>> {
|
package/package.json
CHANGED