@ragable/sdk 0.3.0 → 0.4.1

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
@@ -3,6 +3,12 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
5
  // src/request-client.ts
6
+ function bindFetch(custom) {
7
+ return (input, init) => {
8
+ const f = custom ?? globalThis.fetch;
9
+ return f.call(globalThis, input, init);
10
+ };
11
+ }
6
12
  var RagableError = class extends Error {
7
13
  constructor(message, status, body) {
8
14
  super(message);
@@ -35,7 +41,7 @@ var RagableRequestClient = class {
35
41
  __publicField(this, "defaultHeaders");
36
42
  this.apiKey = options.apiKey;
37
43
  this.baseUrl = options.baseUrl ?? "http://localhost:8080/api";
38
- this.fetchImpl = options.fetch ?? fetch;
44
+ this.fetchImpl = bindFetch(options.fetch);
39
45
  this.defaultHeaders = options.headers;
40
46
  }
41
47
  toUrl(path) {
@@ -338,6 +344,356 @@ var AgentsClient = class {
338
344
  }
339
345
  };
340
346
 
347
+ // src/browser-postgrest.ts
348
+ function assertIdent(name, ctx) {
349
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
350
+ throw new RagableError(
351
+ `Invalid ${ctx} identifier "${name}" (use letters, numbers, underscore)`,
352
+ 400,
353
+ null
354
+ );
355
+ }
356
+ }
357
+ function quoteIdent(name) {
358
+ assertIdent(name, "column");
359
+ return `"${name}"`;
360
+ }
361
+ var OP_SQL = {
362
+ eq: "=",
363
+ neq: "<>",
364
+ gt: ">",
365
+ gte: ">=",
366
+ lt: "<",
367
+ lte: "<=",
368
+ like: "LIKE",
369
+ ilike: "ILIKE"
370
+ };
371
+ async function asPostgrestResponse(fn) {
372
+ try {
373
+ const data = await fn();
374
+ return { data, error: null };
375
+ } catch (e) {
376
+ const err = e instanceof RagableError ? e : new RagableError(e.message, 500, null);
377
+ return { data: null, error: err };
378
+ }
379
+ }
380
+ function buildWhere(filters, params) {
381
+ if (filters.length === 0) return { clause: "", nextIdx: 1 };
382
+ const parts = [];
383
+ let i = params.length;
384
+ for (const f of filters) {
385
+ assertIdent(f.column, "filter");
386
+ i += 1;
387
+ parts.push(`${quoteIdent(f.column)} ${OP_SQL[f.op]} $${i}`);
388
+ params.push(f.value);
389
+ }
390
+ return { clause: ` WHERE ${parts.join(" AND ")}`, nextIdx: i };
391
+ }
392
+ var PostgrestSelectBuilder = class {
393
+ constructor(run, databaseInstanceId, table, columns) {
394
+ this.run = run;
395
+ this.databaseInstanceId = databaseInstanceId;
396
+ this.table = table;
397
+ this.columns = columns;
398
+ __publicField(this, "filters", []);
399
+ __publicField(this, "_limit");
400
+ __publicField(this, "_order");
401
+ assertIdent(table, "table");
402
+ }
403
+ eq(column, value) {
404
+ this.filters.push({ op: "eq", column, value });
405
+ return this;
406
+ }
407
+ neq(column, value) {
408
+ this.filters.push({ op: "neq", column, value });
409
+ return this;
410
+ }
411
+ gt(column, value) {
412
+ this.filters.push({ op: "gt", column, value });
413
+ return this;
414
+ }
415
+ gte(column, value) {
416
+ this.filters.push({ op: "gte", column, value });
417
+ return this;
418
+ }
419
+ lt(column, value) {
420
+ this.filters.push({ op: "lt", column, value });
421
+ return this;
422
+ }
423
+ lte(column, value) {
424
+ this.filters.push({ op: "lte", column, value });
425
+ return this;
426
+ }
427
+ like(column, value) {
428
+ this.filters.push({ op: "like", column, value });
429
+ return this;
430
+ }
431
+ ilike(column, value) {
432
+ this.filters.push({ op: "ilike", column, value });
433
+ return this;
434
+ }
435
+ limit(n) {
436
+ this._limit = n;
437
+ return this;
438
+ }
439
+ order(column, options) {
440
+ this._order = { column, ascending: options?.ascending !== false };
441
+ return this;
442
+ }
443
+ /** @param includeUserLimit when false, omit `.limit()` (for `.single()` / `.maybeSingle()`). */
444
+ buildSelectCore(params, includeUserLimit) {
445
+ const tbl = quoteIdent(this.table);
446
+ const { clause } = buildWhere(this.filters, params);
447
+ let sql = `SELECT ${this.columns} FROM ${tbl}${clause}`;
448
+ if (this._order) {
449
+ sql += ` ORDER BY ${quoteIdent(this._order.column)} ${this._order.ascending ? "ASC" : "DESC"}`;
450
+ }
451
+ if (includeUserLimit && this._limit != null) {
452
+ sql += ` LIMIT ${Math.max(0, Math.floor(this._limit))}`;
453
+ }
454
+ return sql;
455
+ }
456
+ then(onfulfilled, onrejected) {
457
+ return this.executeMany().then(onfulfilled, onrejected);
458
+ }
459
+ async executeMany() {
460
+ return asPostgrestResponse(async () => {
461
+ const params = [];
462
+ const sql = this.buildSelectCore(params, true);
463
+ const res = await this.run({
464
+ databaseInstanceId: this.databaseInstanceId,
465
+ sql,
466
+ params,
467
+ readOnly: true
468
+ });
469
+ return res.rows;
470
+ });
471
+ }
472
+ async single() {
473
+ return asPostgrestResponse(async () => {
474
+ const params = [];
475
+ const base = this.buildSelectCore(params, false);
476
+ const sql = `${base} LIMIT 2`;
477
+ const res = await this.run({
478
+ databaseInstanceId: this.databaseInstanceId,
479
+ sql,
480
+ params,
481
+ readOnly: true
482
+ });
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) {
489
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
490
+ code: "PGRST116"
491
+ });
492
+ }
493
+ return res.rows[0];
494
+ });
495
+ }
496
+ async maybeSingle() {
497
+ return asPostgrestResponse(async () => {
498
+ const params = [];
499
+ const base = this.buildSelectCore(params, false);
500
+ const sql = `${base} LIMIT 2`;
501
+ const res = await this.run({
502
+ databaseInstanceId: this.databaseInstanceId,
503
+ sql,
504
+ params,
505
+ readOnly: true
506
+ });
507
+ if (res.rows.length > 1) {
508
+ throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
509
+ code: "PGRST116"
510
+ });
511
+ }
512
+ return res.rows[0] ?? null;
513
+ });
514
+ }
515
+ };
516
+ var PostgrestInsertBuilder = class {
517
+ constructor(run, databaseInstanceId, table, rows) {
518
+ this.run = run;
519
+ this.databaseInstanceId = databaseInstanceId;
520
+ this.table = table;
521
+ this.rows = rows;
522
+ __publicField(this, "returning", "*");
523
+ assertIdent(table, "table");
524
+ }
525
+ select(columns = "*") {
526
+ this.returning = columns;
527
+ return this;
528
+ }
529
+ then(onfulfilled, onrejected) {
530
+ return this.execute().then(onfulfilled, onrejected);
531
+ }
532
+ async execute() {
533
+ return asPostgrestResponse(async () => {
534
+ if (this.rows.length === 0) return [];
535
+ const keys = Object.keys(this.rows[0]);
536
+ for (const k of keys) assertIdent(k, "column");
537
+ const tbl = quoteIdent(this.table);
538
+ const params = [];
539
+ const valueGroups = [];
540
+ for (const row of this.rows) {
541
+ const placeholders = [];
542
+ for (const k of keys) {
543
+ params.push(row[k]);
544
+ placeholders.push(`$${params.length}`);
545
+ }
546
+ valueGroups.push(`(${placeholders.join(", ")})`);
547
+ }
548
+ const cols = keys.map(quoteIdent).join(", ");
549
+ const sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")} RETURNING ${this.returning}`;
550
+ const res = await this.run({
551
+ databaseInstanceId: this.databaseInstanceId,
552
+ sql,
553
+ params,
554
+ readOnly: false
555
+ });
556
+ return res.rows;
557
+ });
558
+ }
559
+ };
560
+ var PostgrestUpdateBuilder = class {
561
+ constructor(run, databaseInstanceId, table, patch) {
562
+ this.run = run;
563
+ this.databaseInstanceId = databaseInstanceId;
564
+ this.table = table;
565
+ this.patch = patch;
566
+ __publicField(this, "filters", []);
567
+ __publicField(this, "returning", "*");
568
+ assertIdent(table, "table");
569
+ }
570
+ eq(column, value) {
571
+ this.filters.push({ op: "eq", column, value });
572
+ return this;
573
+ }
574
+ select(columns = "*") {
575
+ this.returning = columns;
576
+ return this;
577
+ }
578
+ then(onfulfilled, onrejected) {
579
+ return this.execute().then(onfulfilled, onrejected);
580
+ }
581
+ async execute() {
582
+ return asPostgrestResponse(async () => {
583
+ const keys = Object.keys(this.patch);
584
+ if (keys.length === 0) {
585
+ throw new RagableError("Empty update payload", 400, null);
586
+ }
587
+ for (const k of keys) assertIdent(k, "column");
588
+ if (this.filters.length === 0) {
589
+ throw new RagableError(
590
+ "UPDATE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped update",
591
+ 400,
592
+ null
593
+ );
594
+ }
595
+ const params = [];
596
+ const sets = [];
597
+ for (const k of keys) {
598
+ params.push(this.patch[k]);
599
+ sets.push(`${quoteIdent(k)} = $${params.length}`);
600
+ }
601
+ const { clause } = buildWhere(this.filters, params);
602
+ const tbl = quoteIdent(this.table);
603
+ const sql = `UPDATE ${tbl} SET ${sets.join(", ")}${clause} RETURNING ${this.returning}`;
604
+ const res = await this.run({
605
+ databaseInstanceId: this.databaseInstanceId,
606
+ sql,
607
+ params,
608
+ readOnly: false
609
+ });
610
+ return res.rows;
611
+ });
612
+ }
613
+ };
614
+ var PostgrestDeleteBuilder = class {
615
+ constructor(run, databaseInstanceId, table) {
616
+ this.run = run;
617
+ this.databaseInstanceId = databaseInstanceId;
618
+ this.table = table;
619
+ __publicField(this, "filters", []);
620
+ __publicField(this, "returning", "*");
621
+ assertIdent(table, "table");
622
+ }
623
+ eq(column, value) {
624
+ this.filters.push({ op: "eq", column, value });
625
+ return this;
626
+ }
627
+ select(columns = "*") {
628
+ this.returning = columns;
629
+ return this;
630
+ }
631
+ then(onfulfilled, onrejected) {
632
+ return this.execute().then(onfulfilled, onrejected);
633
+ }
634
+ async execute() {
635
+ return asPostgrestResponse(async () => {
636
+ if (this.filters.length === 0) {
637
+ throw new RagableError(
638
+ "DELETE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped delete",
639
+ 400,
640
+ null
641
+ );
642
+ }
643
+ const params = [];
644
+ const { clause } = buildWhere(this.filters, params);
645
+ const tbl = quoteIdent(this.table);
646
+ const sql = `DELETE FROM ${tbl}${clause} RETURNING ${this.returning}`;
647
+ const res = await this.run({
648
+ databaseInstanceId: this.databaseInstanceId,
649
+ sql,
650
+ params,
651
+ readOnly: false
652
+ });
653
+ return res.rows;
654
+ });
655
+ }
656
+ };
657
+ var PostgrestTableApi = class {
658
+ constructor(run, databaseInstanceId, table) {
659
+ this.run = run;
660
+ this.databaseInstanceId = databaseInstanceId;
661
+ this.table = table;
662
+ }
663
+ select(columns = "*") {
664
+ return new PostgrestSelectBuilder(
665
+ this.run,
666
+ this.databaseInstanceId,
667
+ this.table,
668
+ columns
669
+ );
670
+ }
671
+ insert(values) {
672
+ const rows = Array.isArray(values) ? values : [values];
673
+ return new PostgrestInsertBuilder(
674
+ this.run,
675
+ this.databaseInstanceId,
676
+ this.table,
677
+ rows
678
+ );
679
+ }
680
+ update(patch) {
681
+ return new PostgrestUpdateBuilder(
682
+ this.run,
683
+ this.databaseInstanceId,
684
+ this.table,
685
+ patch
686
+ );
687
+ }
688
+ delete() {
689
+ return new PostgrestDeleteBuilder(
690
+ this.run,
691
+ this.databaseInstanceId,
692
+ this.table
693
+ );
694
+ }
695
+ };
696
+
341
697
  // src/browser.ts
342
698
  function normalizeBrowserApiBase(baseUrl) {
343
699
  return (baseUrl ?? "http://localhost:8080/api").replace(/\/+$/, "");
@@ -372,12 +728,32 @@ async function parseJsonOrThrow(response) {
372
728
  }
373
729
  return payload;
374
730
  }
731
+ function parseExpiresInSeconds(expiresIn) {
732
+ const s = expiresIn.trim().toLowerCase();
733
+ const m = /^(\d+)([smhd])?$/.exec(s);
734
+ if (m) {
735
+ const n = Number(m[1]);
736
+ const u = m[2] ?? "s";
737
+ const mult = u === "s" ? 1 : u === "m" ? 60 : u === "h" ? 3600 : u === "d" ? 86400 : 1;
738
+ return n * mult;
739
+ }
740
+ const asNum = Number(s);
741
+ return Number.isFinite(asNum) ? asNum : 0;
742
+ }
743
+ function toSupabaseSession(s) {
744
+ return {
745
+ access_token: s.accessToken,
746
+ refresh_token: s.refreshToken,
747
+ expires_in: parseExpiresInSeconds(s.expiresIn),
748
+ token_type: "bearer",
749
+ user: s.user
750
+ };
751
+ }
375
752
  var RagableBrowserAuthClient = class {
376
753
  constructor(options) {
377
754
  this.options = options;
378
- }
379
- get fetchImpl() {
380
- return this.options.fetch ?? fetch;
755
+ __publicField(this, "fetchImpl");
756
+ this.fetchImpl = bindFetch(options.fetch);
381
757
  }
382
758
  toUrl(path) {
383
759
  return `${normalizeBrowserApiBase(this.options.baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
@@ -393,6 +769,69 @@ var RagableBrowserAuthClient = class {
393
769
  const gid = requireAuthGroupId(this.options);
394
770
  return `/auth-groups/${gid}/auth`;
395
771
  }
772
+ /** Supabase: `signUp` → `{ data: { user, session }, error }` */
773
+ async signUp(credentials) {
774
+ return asPostgrestResponse(async () => {
775
+ const name = typeof credentials.options?.data?.name === "string" ? credentials.options.data.name : void 0;
776
+ const session = await this.register({
777
+ email: credentials.email,
778
+ password: credentials.password,
779
+ name
780
+ });
781
+ return { user: session.user, session: toSupabaseSession(session) };
782
+ });
783
+ }
784
+ /** Supabase: `signInWithPassword` */
785
+ async signInWithPassword(credentials) {
786
+ return asPostgrestResponse(async () => {
787
+ const session = await this.login(credentials);
788
+ return { user: session.user, session: toSupabaseSession(session) };
789
+ });
790
+ }
791
+ /** Supabase: `refreshSession` */
792
+ async refreshSession(refreshToken) {
793
+ return asPostgrestResponse(async () => {
794
+ const tokens = await this.refresh({ refreshToken });
795
+ const me = await this.getUserFromToken(tokens.accessToken);
796
+ const session = {
797
+ access_token: tokens.accessToken,
798
+ refresh_token: tokens.refreshToken,
799
+ expires_in: parseExpiresInSeconds(tokens.expiresIn),
800
+ token_type: "bearer",
801
+ user: me.user
802
+ };
803
+ return { session, user: me.user };
804
+ });
805
+ }
806
+ /** Supabase: `getUser()` — needs `getAccessToken` on the client */
807
+ async getUser() {
808
+ return asPostgrestResponse(() => this.getMe());
809
+ }
810
+ /** Supabase: `updateUser` */
811
+ async updateUser(attributes) {
812
+ return asPostgrestResponse(
813
+ () => this.updateMe({
814
+ password: attributes.password,
815
+ name: attributes.data?.name
816
+ })
817
+ );
818
+ }
819
+ /**
820
+ * Supabase/Firebase: no server call — clear tokens in your app.
821
+ * Returns `{ error: null }` for API compatibility.
822
+ */
823
+ async signOut(_options) {
824
+ return { error: null };
825
+ }
826
+ async getUserFromToken(accessToken) {
827
+ const headers = this.baseHeaders(false);
828
+ headers.set("Authorization", `Bearer ${accessToken}`);
829
+ const response = await this.fetchImpl(`${this.toUrl(this.authPrefix())}/me`, {
830
+ method: "GET",
831
+ headers
832
+ });
833
+ return parseJsonOrThrow(response);
834
+ }
396
835
  async register(body) {
397
836
  const response = await this.fetchImpl(`${this.toUrl(this.authPrefix())}/register`, {
398
837
  method: "POST",
@@ -442,9 +881,8 @@ var RagableBrowserAuthClient = class {
442
881
  var RagableBrowserDatabaseClient = class {
443
882
  constructor(options) {
444
883
  this.options = options;
445
- }
446
- get fetchImpl() {
447
- return this.options.fetch ?? fetch;
884
+ __publicField(this, "fetchImpl");
885
+ this.fetchImpl = bindFetch(options.fetch);
448
886
  }
449
887
  toUrl(path) {
450
888
  return `${normalizeBrowserApiBase(this.options.baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
@@ -464,6 +902,7 @@ var RagableBrowserDatabaseClient = class {
464
902
  databaseInstanceId: params.databaseInstanceId,
465
903
  sql: params.sql,
466
904
  ...params.params !== void 0 ? { params: params.params } : {},
905
+ ...params.readOnly === false ? { readOnly: false } : {},
467
906
  ...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
468
907
  ...params.rowLimit !== void 0 ? { rowLimit: params.rowLimit } : {}
469
908
  })
@@ -478,9 +917,8 @@ var RagableBrowserDatabaseClient = class {
478
917
  var RagableBrowserAgentsClient = class {
479
918
  constructor(options) {
480
919
  this.options = options;
481
- }
482
- get fetchImpl() {
483
- return this.options.fetch ?? fetch;
920
+ __publicField(this, "fetchImpl");
921
+ this.fetchImpl = bindFetch(options.fetch);
484
922
  }
485
923
  toUrl(path) {
486
924
  return `${normalizeBrowserApiBase(this.options.baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
@@ -523,14 +961,31 @@ var RagableBrowser = class {
523
961
  __publicField(this, "agents");
524
962
  __publicField(this, "auth");
525
963
  __publicField(this, "database");
964
+ __publicField(this, "options");
965
+ this.options = options;
526
966
  this.agents = new RagableBrowserAgentsClient(options);
527
967
  this.auth = new RagableBrowserAuthClient(options);
528
968
  this.database = new RagableBrowserDatabaseClient(options);
529
969
  }
970
+ /**
971
+ * Supabase-style table API: `.from('items').select().eq('id', 1).single()`.
972
+ * Pass `databaseInstanceId` here or set `databaseInstanceId` on the client options.
973
+ */
974
+ from(table, databaseInstanceId) {
975
+ const id = databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
976
+ if (!id) {
977
+ throw new Error(
978
+ "RagableBrowser.from() requires databaseInstanceId in client options or as the second argument"
979
+ );
980
+ }
981
+ const run = (p) => this.database.query(p);
982
+ return new PostgrestTableApi(run, id, table);
983
+ }
530
984
  };
531
985
  function createBrowserClient(options) {
532
986
  return new RagableBrowser(options);
533
987
  }
988
+ var createRagableBrowserClient = createBrowserClient;
534
989
 
535
990
  // src/rag.ts
536
991
  function formatRetrievalContext(results, options = {}) {
@@ -588,11 +1043,45 @@ var Ragable = class {
588
1043
  };
589
1044
  }
590
1045
  };
591
- function createClient(options) {
1046
+ function isServerClientOptions(o) {
1047
+ return typeof o === "object" && o !== null && "apiKey" in o && typeof o.apiKey === "string" && o.apiKey.length > 0;
1048
+ }
1049
+ function createClient(urlOrOptions, browserOptions) {
1050
+ if (typeof urlOrOptions === "string") {
1051
+ if (!browserOptions) {
1052
+ throw new Error(
1053
+ "createClient(url, options) requires options with at least organizationId"
1054
+ );
1055
+ }
1056
+ const raw = urlOrOptions.trim().replace(/\/+$/, "");
1057
+ const baseUrl = raw.endsWith("/api") ? raw : `${raw}/api`;
1058
+ return createBrowserClient({
1059
+ ...browserOptions,
1060
+ baseUrl: normalizeBrowserApiBase(baseUrl)
1061
+ });
1062
+ }
1063
+ if (isServerClientOptions(urlOrOptions)) {
1064
+ return new Ragable(urlOrOptions);
1065
+ }
1066
+ if (typeof urlOrOptions === "object" && urlOrOptions !== null && "organizationId" in urlOrOptions && typeof urlOrOptions.organizationId === "string") {
1067
+ return createBrowserClient(
1068
+ urlOrOptions
1069
+ );
1070
+ }
1071
+ throw new Error(
1072
+ "createClient(options) requires apiKey (server) or organizationId (browser)"
1073
+ );
1074
+ }
1075
+ function createRagableServerClient(options) {
592
1076
  return new Ragable(options);
593
1077
  }
594
1078
  export {
595
1079
  AgentsClient,
1080
+ PostgrestDeleteBuilder,
1081
+ PostgrestInsertBuilder,
1082
+ PostgrestSelectBuilder,
1083
+ PostgrestTableApi,
1084
+ PostgrestUpdateBuilder,
596
1085
  Ragable,
597
1086
  RagableBrowser,
598
1087
  RagableBrowserAgentsClient,
@@ -601,9 +1090,13 @@ export {
601
1090
  RagableError,
602
1091
  RagableRequestClient,
603
1092
  ShiftClient,
1093
+ asPostgrestResponse,
1094
+ bindFetch,
604
1095
  createBrowserClient,
605
1096
  createClient,
606
1097
  createRagPipeline,
1098
+ createRagableBrowserClient,
1099
+ createRagableServerClient,
607
1100
  extractErrorMessage,
608
1101
  formatRetrievalContext,
609
1102
  normalizeBrowserApiBase,