@ragable/sdk 0.4.1 → 0.5.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/dist/index.mjs CHANGED
@@ -358,6 +358,18 @@ function quoteIdent(name) {
358
358
  assertIdent(name, "column");
359
359
  return `"${name}"`;
360
360
  }
361
+ function parseConflictColumns(onConflict) {
362
+ const parts = onConflict.split(",").map((s) => s.trim()).filter(Boolean);
363
+ if (parts.length === 0) {
364
+ throw new RagableError(
365
+ "upsert requires onConflict with at least one column (e.g. 'id' or 'org_id,user_id')",
366
+ 400,
367
+ null
368
+ );
369
+ }
370
+ for (const p of parts) assertIdent(p, "onConflict");
371
+ return parts;
372
+ }
361
373
  var OP_SQL = {
362
374
  eq: "=",
363
375
  neq: "<>",
@@ -440,7 +452,6 @@ var PostgrestSelectBuilder = class {
440
452
  this._order = { column, ascending: options?.ascending !== false };
441
453
  return this;
442
454
  }
443
- /** @param includeUserLimit when false, omit `.limit()` (for `.single()` / `.maybeSingle()`). */
444
455
  buildSelectCore(params, includeUserLimit) {
445
456
  const tbl = quoteIdent(this.table);
446
457
  const { clause } = buildWhere(this.filters, params);
@@ -480,12 +491,7 @@ var PostgrestSelectBuilder = class {
480
491
  params,
481
492
  readOnly: true
482
493
  });
483
- if (res.rows.length === 0) {
484
- throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
485
- code: "PGRST116"
486
- });
487
- }
488
- if (res.rows.length > 1) {
494
+ if (res.rows.length === 0 || res.rows.length > 1) {
489
495
  throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
490
496
  code: "PGRST116"
491
497
  });
@@ -513,23 +519,70 @@ var PostgrestSelectBuilder = class {
513
519
  });
514
520
  }
515
521
  };
516
- var PostgrestInsertBuilder = class {
522
+ var PostgrestInsertRootBuilder = class {
517
523
  constructor(run, databaseInstanceId, table, rows) {
518
524
  this.run = run;
519
525
  this.databaseInstanceId = databaseInstanceId;
520
526
  this.table = table;
521
527
  this.rows = rows;
522
- __publicField(this, "returning", "*");
523
528
  assertIdent(table, "table");
524
529
  }
530
+ /**
531
+ * Return inserted rows (Supabase: chain `.select()` to get data).
532
+ * @see https://supabase.com/docs/reference/javascript/insert
533
+ */
525
534
  select(columns = "*") {
526
- this.returning = columns;
527
- return this;
535
+ return new PostgrestInsertReturningBuilder(
536
+ this.run,
537
+ this.databaseInstanceId,
538
+ this.table,
539
+ this.rows,
540
+ columns
541
+ );
542
+ }
543
+ then(onfulfilled, onrejected) {
544
+ return this.executeNoReturn().then(onfulfilled, onrejected);
545
+ }
546
+ async executeNoReturn() {
547
+ return asPostgrestResponse(async () => {
548
+ if (this.rows.length === 0) return null;
549
+ const keys = Object.keys(this.rows[0]);
550
+ for (const k of keys) assertIdent(k, "column");
551
+ const tbl = quoteIdent(this.table);
552
+ const params = [];
553
+ const valueGroups = [];
554
+ for (const row of this.rows) {
555
+ const placeholders = [];
556
+ for (const k of keys) {
557
+ params.push(row[k]);
558
+ placeholders.push(`$${params.length}`);
559
+ }
560
+ valueGroups.push(`(${placeholders.join(", ")})`);
561
+ }
562
+ const cols = keys.map(quoteIdent).join(", ");
563
+ const sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")}`;
564
+ await this.run({
565
+ databaseInstanceId: this.databaseInstanceId,
566
+ sql,
567
+ params,
568
+ readOnly: false
569
+ });
570
+ return null;
571
+ });
572
+ }
573
+ };
574
+ var PostgrestInsertReturningBuilder = class {
575
+ constructor(run, databaseInstanceId, table, rows, returning) {
576
+ this.run = run;
577
+ this.databaseInstanceId = databaseInstanceId;
578
+ this.table = table;
579
+ this.rows = rows;
580
+ this.returning = returning;
528
581
  }
529
582
  then(onfulfilled, onrejected) {
530
- return this.execute().then(onfulfilled, onrejected);
583
+ return this.executeMany().then(onfulfilled, onrejected);
531
584
  }
532
- async execute() {
585
+ async executeMany() {
533
586
  return asPostgrestResponse(async () => {
534
587
  if (this.rows.length === 0) return [];
535
588
  const keys = Object.keys(this.rows[0]);
@@ -556,29 +609,137 @@ var PostgrestInsertBuilder = class {
556
609
  return res.rows;
557
610
  });
558
611
  }
612
+ async single() {
613
+ return asPostgrestResponse(async () => {
614
+ const { data, error } = await this.executeMany();
615
+ if (error) throw error;
616
+ const rows = data ?? [];
617
+ if (rows.length === 0 || rows.length > 1) {
618
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
619
+ code: "PGRST116"
620
+ });
621
+ }
622
+ return rows[0];
623
+ });
624
+ }
625
+ async maybeSingle() {
626
+ return asPostgrestResponse(async () => {
627
+ const { data, error } = await this.executeMany();
628
+ if (error) throw error;
629
+ const rows = data ?? [];
630
+ if (rows.length > 1) {
631
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
632
+ code: "PGRST116"
633
+ });
634
+ }
635
+ return rows[0] ?? null;
636
+ });
637
+ }
559
638
  };
560
- var PostgrestUpdateBuilder = class {
639
+ var PostgrestUpdateRootBuilder = class {
561
640
  constructor(run, databaseInstanceId, table, patch) {
562
641
  this.run = run;
563
642
  this.databaseInstanceId = databaseInstanceId;
564
643
  this.table = table;
565
644
  this.patch = patch;
566
645
  __publicField(this, "filters", []);
567
- __publicField(this, "returning", "*");
568
646
  assertIdent(table, "table");
569
647
  }
570
648
  eq(column, value) {
571
649
  this.filters.push({ op: "eq", column, value });
572
650
  return this;
573
651
  }
574
- select(columns = "*") {
575
- this.returning = columns;
652
+ neq(column, value) {
653
+ this.filters.push({ op: "neq", column, value });
654
+ return this;
655
+ }
656
+ gt(column, value) {
657
+ this.filters.push({ op: "gt", column, value });
658
+ return this;
659
+ }
660
+ gte(column, value) {
661
+ this.filters.push({ op: "gte", column, value });
662
+ return this;
663
+ }
664
+ lt(column, value) {
665
+ this.filters.push({ op: "lt", column, value });
666
+ return this;
667
+ }
668
+ lte(column, value) {
669
+ this.filters.push({ op: "lte", column, value });
670
+ return this;
671
+ }
672
+ like(column, value) {
673
+ this.filters.push({ op: "like", column, value });
674
+ return this;
675
+ }
676
+ ilike(column, value) {
677
+ this.filters.push({ op: "ilike", column, value });
576
678
  return this;
577
679
  }
680
+ /**
681
+ * Return updated rows (Supabase: `.update().eq().select()`).
682
+ * @see https://supabase.com/docs/reference/javascript/update
683
+ */
684
+ select(columns = "*") {
685
+ return new PostgrestUpdateReturningBuilder(
686
+ this.run,
687
+ this.databaseInstanceId,
688
+ this.table,
689
+ this.patch,
690
+ this.filters,
691
+ columns
692
+ );
693
+ }
694
+ then(onfulfilled, onrejected) {
695
+ return this.executeNoReturn().then(onfulfilled, onrejected);
696
+ }
697
+ async executeNoReturn() {
698
+ return asPostgrestResponse(async () => {
699
+ const keys = Object.keys(this.patch);
700
+ if (keys.length === 0) {
701
+ throw new RagableError("Empty update payload", 400, null);
702
+ }
703
+ for (const k of keys) assertIdent(k, "column");
704
+ if (this.filters.length === 0) {
705
+ throw new RagableError(
706
+ "UPDATE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped update",
707
+ 400,
708
+ null
709
+ );
710
+ }
711
+ const params = [];
712
+ const sets = [];
713
+ for (const k of keys) {
714
+ params.push(this.patch[k]);
715
+ sets.push(`${quoteIdent(k)} = $${params.length}`);
716
+ }
717
+ const { clause } = buildWhere(this.filters, params);
718
+ const tbl = quoteIdent(this.table);
719
+ const sql = `UPDATE ${tbl} SET ${sets.join(", ")}${clause}`;
720
+ await this.run({
721
+ databaseInstanceId: this.databaseInstanceId,
722
+ sql,
723
+ params,
724
+ readOnly: false
725
+ });
726
+ return null;
727
+ });
728
+ }
729
+ };
730
+ var PostgrestUpdateReturningBuilder = class {
731
+ constructor(run, databaseInstanceId, table, patch, filters, returning) {
732
+ this.run = run;
733
+ this.databaseInstanceId = databaseInstanceId;
734
+ this.table = table;
735
+ this.patch = patch;
736
+ this.filters = filters;
737
+ this.returning = returning;
738
+ }
578
739
  then(onfulfilled, onrejected) {
579
- return this.execute().then(onfulfilled, onrejected);
740
+ return this.executeMany().then(onfulfilled, onrejected);
580
741
  }
581
- async execute() {
742
+ async executeMany() {
582
743
  return asPostgrestResponse(async () => {
583
744
  const keys = Object.keys(this.patch);
584
745
  if (keys.length === 0) {
@@ -610,28 +771,124 @@ var PostgrestUpdateBuilder = class {
610
771
  return res.rows;
611
772
  });
612
773
  }
774
+ async single() {
775
+ return asPostgrestResponse(async () => {
776
+ const { data, error } = await this.executeMany();
777
+ if (error) throw error;
778
+ const rows = data ?? [];
779
+ if (rows.length === 0 || rows.length > 1) {
780
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
781
+ code: "PGRST116"
782
+ });
783
+ }
784
+ return rows[0];
785
+ });
786
+ }
787
+ async maybeSingle() {
788
+ return asPostgrestResponse(async () => {
789
+ const { data, error } = await this.executeMany();
790
+ if (error) throw error;
791
+ const rows = data ?? [];
792
+ if (rows.length > 1) {
793
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
794
+ code: "PGRST116"
795
+ });
796
+ }
797
+ return rows[0] ?? null;
798
+ });
799
+ }
613
800
  };
614
- var PostgrestDeleteBuilder = class {
801
+ var PostgrestDeleteRootBuilder = class {
615
802
  constructor(run, databaseInstanceId, table) {
616
803
  this.run = run;
617
804
  this.databaseInstanceId = databaseInstanceId;
618
805
  this.table = table;
619
806
  __publicField(this, "filters", []);
620
- __publicField(this, "returning", "*");
621
807
  assertIdent(table, "table");
622
808
  }
623
809
  eq(column, value) {
624
810
  this.filters.push({ op: "eq", column, value });
625
811
  return this;
626
812
  }
627
- select(columns = "*") {
628
- this.returning = columns;
813
+ neq(column, value) {
814
+ this.filters.push({ op: "neq", column, value });
815
+ return this;
816
+ }
817
+ gt(column, value) {
818
+ this.filters.push({ op: "gt", column, value });
819
+ return this;
820
+ }
821
+ gte(column, value) {
822
+ this.filters.push({ op: "gte", column, value });
823
+ return this;
824
+ }
825
+ lt(column, value) {
826
+ this.filters.push({ op: "lt", column, value });
827
+ return this;
828
+ }
829
+ lte(column, value) {
830
+ this.filters.push({ op: "lte", column, value });
629
831
  return this;
630
832
  }
833
+ like(column, value) {
834
+ this.filters.push({ op: "like", column, value });
835
+ return this;
836
+ }
837
+ ilike(column, value) {
838
+ this.filters.push({ op: "ilike", column, value });
839
+ return this;
840
+ }
841
+ /**
842
+ * Return deleted rows (Supabase: `.delete().eq().select()`).
843
+ * @see https://supabase.com/docs/reference/javascript/delete
844
+ */
845
+ select(columns = "*") {
846
+ return new PostgrestDeleteReturningBuilder(
847
+ this.run,
848
+ this.databaseInstanceId,
849
+ this.table,
850
+ this.filters,
851
+ columns
852
+ );
853
+ }
854
+ then(onfulfilled, onrejected) {
855
+ return this.executeNoReturn().then(onfulfilled, onrejected);
856
+ }
857
+ async executeNoReturn() {
858
+ return asPostgrestResponse(async () => {
859
+ if (this.filters.length === 0) {
860
+ throw new RagableError(
861
+ "DELETE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped delete",
862
+ 400,
863
+ null
864
+ );
865
+ }
866
+ const params = [];
867
+ const { clause } = buildWhere(this.filters, params);
868
+ const tbl = quoteIdent(this.table);
869
+ const sql = `DELETE FROM ${tbl}${clause}`;
870
+ await this.run({
871
+ databaseInstanceId: this.databaseInstanceId,
872
+ sql,
873
+ params,
874
+ readOnly: false
875
+ });
876
+ return null;
877
+ });
878
+ }
879
+ };
880
+ var PostgrestDeleteReturningBuilder = class {
881
+ constructor(run, databaseInstanceId, table, filters, returning) {
882
+ this.run = run;
883
+ this.databaseInstanceId = databaseInstanceId;
884
+ this.table = table;
885
+ this.filters = filters;
886
+ this.returning = returning;
887
+ }
631
888
  then(onfulfilled, onrejected) {
632
- return this.execute().then(onfulfilled, onrejected);
889
+ return this.executeMany().then(onfulfilled, onrejected);
633
890
  }
634
- async execute() {
891
+ async executeMany() {
635
892
  return asPostgrestResponse(async () => {
636
893
  if (this.filters.length === 0) {
637
894
  throw new RagableError(
@@ -653,6 +910,145 @@ var PostgrestDeleteBuilder = class {
653
910
  return res.rows;
654
911
  });
655
912
  }
913
+ async single() {
914
+ return asPostgrestResponse(async () => {
915
+ const { data, error } = await this.executeMany();
916
+ if (error) throw error;
917
+ const rows = data ?? [];
918
+ if (rows.length === 0 || rows.length > 1) {
919
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
920
+ code: "PGRST116"
921
+ });
922
+ }
923
+ return rows[0];
924
+ });
925
+ }
926
+ async maybeSingle() {
927
+ return asPostgrestResponse(async () => {
928
+ const { data, error } = await this.executeMany();
929
+ if (error) throw error;
930
+ const rows = data ?? [];
931
+ if (rows.length > 1) {
932
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
933
+ code: "PGRST116"
934
+ });
935
+ }
936
+ return rows[0] ?? null;
937
+ });
938
+ }
939
+ };
940
+ var PostgrestUpsertRootBuilder = class {
941
+ constructor(run, databaseInstanceId, table, rows, onConflict, ignoreDuplicates) {
942
+ this.run = run;
943
+ this.databaseInstanceId = databaseInstanceId;
944
+ this.table = table;
945
+ this.rows = rows;
946
+ this.ignoreDuplicates = ignoreDuplicates;
947
+ __publicField(this, "conflictCols");
948
+ assertIdent(table, "table");
949
+ this.conflictCols = parseConflictColumns(onConflict);
950
+ }
951
+ /**
952
+ * Return upserted rows (Supabase: `.upsert().select()`).
953
+ * @see https://supabase.com/docs/reference/javascript/upsert
954
+ */
955
+ select(columns = "*") {
956
+ return new PostgrestUpsertReturningBuilder(this, columns);
957
+ }
958
+ then(onfulfilled, onrejected) {
959
+ return this.executeNoReturn().then(onfulfilled, onrejected);
960
+ }
961
+ async executeNoReturn() {
962
+ return asPostgrestResponse(async () => {
963
+ await this.runUpsert(null);
964
+ return null;
965
+ });
966
+ }
967
+ async runUpsert(returning) {
968
+ if (this.rows.length === 0) {
969
+ return { command: "INSERT", rowCount: 0, truncated: false, rows: [] };
970
+ }
971
+ const keys = Object.keys(this.rows[0]);
972
+ for (const k of keys) assertIdent(k, "column");
973
+ const tbl = quoteIdent(this.table);
974
+ const conflictQuoted = this.conflictCols.map(quoteIdent).join(", ");
975
+ const params = [];
976
+ const valueGroups = [];
977
+ for (const row of this.rows) {
978
+ const placeholders = [];
979
+ for (const k of keys) {
980
+ params.push(row[k]);
981
+ placeholders.push(`$${params.length}`);
982
+ }
983
+ valueGroups.push(`(${placeholders.join(", ")})`);
984
+ }
985
+ const cols = keys.map(quoteIdent).join(", ");
986
+ let sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")} ON CONFLICT (${conflictQuoted})`;
987
+ if (this.ignoreDuplicates) {
988
+ sql += " DO NOTHING";
989
+ } else {
990
+ const setParts = keys.filter((k) => !this.conflictCols.includes(k)).map((k) => `${quoteIdent(k)} = EXCLUDED.${quoteIdent(k)}`);
991
+ if (setParts.length === 0) {
992
+ sql += " DO NOTHING";
993
+ } else {
994
+ sql += ` DO UPDATE SET ${setParts.join(", ")}`;
995
+ }
996
+ }
997
+ if (returning) {
998
+ sql += ` RETURNING ${returning}`;
999
+ }
1000
+ return this.run({
1001
+ databaseInstanceId: this.databaseInstanceId,
1002
+ sql,
1003
+ params,
1004
+ readOnly: false
1005
+ });
1006
+ }
1007
+ /** Used by returning builder */
1008
+ async runWithReturning(returning) {
1009
+ return this.runUpsert(returning);
1010
+ }
1011
+ };
1012
+ var PostgrestUpsertReturningBuilder = class {
1013
+ constructor(root, returning) {
1014
+ this.root = root;
1015
+ this.returning = returning;
1016
+ }
1017
+ then(onfulfilled, onrejected) {
1018
+ return this.executeMany().then(onfulfilled, onrejected);
1019
+ }
1020
+ async executeMany() {
1021
+ return asPostgrestResponse(async () => {
1022
+ const res = await this.root.runWithReturning(this.returning);
1023
+ return res.rows;
1024
+ });
1025
+ }
1026
+ async single() {
1027
+ return asPostgrestResponse(async () => {
1028
+ const { data, error } = await this.executeMany();
1029
+ if (error) throw error;
1030
+ const rows = data ?? [];
1031
+ if (rows.length === 0 || rows.length > 1) {
1032
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
1033
+ code: "PGRST116"
1034
+ });
1035
+ }
1036
+ return rows[0];
1037
+ });
1038
+ }
1039
+ async maybeSingle() {
1040
+ return asPostgrestResponse(async () => {
1041
+ const { data, error } = await this.executeMany();
1042
+ if (error) throw error;
1043
+ const rows = data ?? [];
1044
+ if (rows.length > 1) {
1045
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
1046
+ code: "PGRST116"
1047
+ });
1048
+ }
1049
+ return rows[0] ?? null;
1050
+ });
1051
+ }
656
1052
  };
657
1053
  var PostgrestTableApi = class {
658
1054
  constructor(run, databaseInstanceId, table) {
@@ -670,7 +1066,7 @@ var PostgrestTableApi = class {
670
1066
  }
671
1067
  insert(values) {
672
1068
  const rows = Array.isArray(values) ? values : [values];
673
- return new PostgrestInsertBuilder(
1069
+ return new PostgrestInsertRootBuilder(
674
1070
  this.run,
675
1071
  this.databaseInstanceId,
676
1072
  this.table,
@@ -678,7 +1074,7 @@ var PostgrestTableApi = class {
678
1074
  );
679
1075
  }
680
1076
  update(patch) {
681
- return new PostgrestUpdateBuilder(
1077
+ return new PostgrestUpdateRootBuilder(
682
1078
  this.run,
683
1079
  this.databaseInstanceId,
684
1080
  this.table,
@@ -686,12 +1082,27 @@ var PostgrestTableApi = class {
686
1082
  );
687
1083
  }
688
1084
  delete() {
689
- return new PostgrestDeleteBuilder(
1085
+ return new PostgrestDeleteRootBuilder(
690
1086
  this.run,
691
1087
  this.databaseInstanceId,
692
1088
  this.table
693
1089
  );
694
1090
  }
1091
+ /**
1092
+ * `INSERT ... ON CONFLICT ... DO UPDATE` (or `DO NOTHING` with `ignoreDuplicates`).
1093
+ * @see https://supabase.com/docs/reference/javascript/upsert
1094
+ */
1095
+ upsert(values, options) {
1096
+ const rows = Array.isArray(values) ? values : [values];
1097
+ return new PostgrestUpsertRootBuilder(
1098
+ this.run,
1099
+ this.databaseInstanceId,
1100
+ this.table,
1101
+ rows,
1102
+ options.onConflict,
1103
+ options.ignoreDuplicates === true
1104
+ );
1105
+ }
695
1106
  };
696
1107
 
697
1108
  // src/browser.ts
@@ -890,6 +1301,12 @@ var RagableBrowserDatabaseClient = class {
890
1301
  async query(params) {
891
1302
  const gid = requireAuthGroupId(this.options);
892
1303
  const token = await requireAccessToken(this.options);
1304
+ const databaseInstanceId = params.databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
1305
+ if (!databaseInstanceId) {
1306
+ throw new Error(
1307
+ "database.query requires databaseInstanceId in the request or on createBrowserClient({ databaseInstanceId })"
1308
+ );
1309
+ }
893
1310
  const headers = this.baseHeaders();
894
1311
  headers.set("Authorization", `Bearer ${token}`);
895
1312
  headers.set("Content-Type", "application/json");
@@ -899,7 +1316,7 @@ var RagableBrowserDatabaseClient = class {
899
1316
  method: "POST",
900
1317
  headers,
901
1318
  body: JSON.stringify({
902
- databaseInstanceId: params.databaseInstanceId,
1319
+ databaseInstanceId,
903
1320
  sql: params.sql,
904
1321
  ...params.params !== void 0 ? { params: params.params } : {},
905
1322
  ...params.readOnly === false ? { readOnly: false } : {},
@@ -1077,11 +1494,16 @@ function createRagableServerClient(options) {
1077
1494
  }
1078
1495
  export {
1079
1496
  AgentsClient,
1080
- PostgrestDeleteBuilder,
1081
- PostgrestInsertBuilder,
1497
+ PostgrestDeleteReturningBuilder,
1498
+ PostgrestDeleteRootBuilder,
1499
+ PostgrestInsertReturningBuilder,
1500
+ PostgrestInsertRootBuilder,
1082
1501
  PostgrestSelectBuilder,
1083
1502
  PostgrestTableApi,
1084
- PostgrestUpdateBuilder,
1503
+ PostgrestUpdateReturningBuilder,
1504
+ PostgrestUpdateRootBuilder,
1505
+ PostgrestUpsertReturningBuilder,
1506
+ PostgrestUpsertRootBuilder,
1085
1507
  Ragable,
1086
1508
  RagableBrowser,
1087
1509
  RagableBrowserAgentsClient,