@basictech/react 0.7.0-beta.2 → 0.7.0-beta.3

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.js CHANGED
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- BasicDBSDK: () => BasicDBSDK,
24
23
  BasicProvider: () => BasicProvider,
25
24
  useBasic: () => useBasic,
26
25
  useQuery: () => import_dexie_react_hooks.useLiveQuery
@@ -279,363 +278,8 @@ var BasicSync = class extends import_dexie2.Dexie {
279
278
  }
280
279
  };
281
280
 
282
- // src/db_ts.ts
283
- var DBError = class extends Error {
284
- constructor(message, status, response, originalError) {
285
- super(message);
286
- this.status = status;
287
- this.response = response;
288
- this.originalError = originalError;
289
- this.name = "DBError";
290
- }
291
- };
292
- var QueryBuilder = class {
293
- constructor(tableClient, tableSchema) {
294
- this.tableClient = tableClient;
295
- this.tableSchema = tableSchema;
296
- }
297
- params = {};
298
- // Reserved fields that are always allowed
299
- reservedFields = ["created_at", "updated_at", "id"];
300
- // Validate field existence in schema
301
- validateField(field) {
302
- if (this.tableSchema && !this.reservedFields.includes(field)) {
303
- if (!this.tableSchema.fields || !(field in this.tableSchema.fields)) {
304
- throw new Error(`Invalid field: "${field}". Field does not exist in table schema.`);
305
- }
306
- }
307
- }
308
- // Validate operator based on field type
309
- validateOperator(field, operator, value) {
310
- if (!this.tableSchema || this.reservedFields.includes(field)) {
311
- return;
312
- }
313
- const fieldInfo = this.tableSchema.fields[field];
314
- if (!fieldInfo)
315
- return;
316
- switch (operator) {
317
- case "gt":
318
- case "gte":
319
- case "lt":
320
- case "lte":
321
- if (fieldInfo.type !== "number" && fieldInfo.type !== "string") {
322
- throw new Error(`Operator "${operator}" can only be used with number or string fields. Field "${field}" is type "${fieldInfo.type}".`);
323
- }
324
- break;
325
- case "like":
326
- case "ilike":
327
- if (fieldInfo.type !== "string") {
328
- throw new Error(`Operator "${operator}" can only be used with string fields. Field "${field}" is type "${fieldInfo.type}".`);
329
- }
330
- if (typeof value !== "string") {
331
- throw new Error(`Operator "${operator}" requires a string value. Received: ${typeof value}`);
332
- }
333
- break;
334
- case "in":
335
- if (!Array.isArray(value)) {
336
- throw new Error(`Operator "in" requires an array value. Received: ${typeof value}`);
337
- }
338
- break;
339
- case "is":
340
- if (value !== null && typeof value !== "boolean") {
341
- throw new Error(`Operator "is" requires null or boolean. Received: ${typeof value}`);
342
- }
343
- break;
344
- }
345
- }
346
- // Add ordering to query with schema validation
347
- order(field, direction = "asc") {
348
- this.validateField(field);
349
- this.params.order = `${field}.${direction}`;
350
- return this;
351
- }
352
- // Add filtering to query
353
- filter(conditions) {
354
- if (!this.params.filters) {
355
- this.params.filters = {};
356
- }
357
- for (const [field, condition] of Object.entries(conditions)) {
358
- this.validateField(field);
359
- if (condition === null || typeof condition !== "object") {
360
- this.params.filters[field] = condition;
361
- } else {
362
- this.params.filters[field] = condition;
363
- }
364
- }
365
- return this;
366
- }
367
- // Add limit to query
368
- limit(count) {
369
- this.params.limit = count;
370
- return this;
371
- }
372
- // Add offset to query for pagination
373
- offset(count) {
374
- this.params.offset = count;
375
- return this;
376
- }
377
- // Auto-execute when awaited
378
- then(onfulfilled, onrejected) {
379
- return this.tableClient.executeQuery(this.params).then(onfulfilled, onrejected);
380
- }
381
- // Auto-execute when awaited with catch
382
- catch(onrejected) {
383
- return this.tableClient.executeQuery(this.params).catch(onrejected);
384
- }
385
- // Auto-execute when awaited with finally
386
- finally(onfinally) {
387
- return this.tableClient.executeQuery(this.params).finally(onfinally);
388
- }
389
- };
390
- var TableClient = class {
391
- constructor(baseUrl, projectId, token, table, getToken, schema) {
392
- this.baseUrl = baseUrl;
393
- this.projectId = projectId;
394
- this.token = token;
395
- this.table = table;
396
- this.getToken = getToken;
397
- this.schema = schema;
398
- if (schema && schema.tables && schema.tables[table]) {
399
- this.tableSchema = schema.tables[table];
400
- }
401
- }
402
- tableSchema;
403
- async headers() {
404
- const token = await this.getToken();
405
- return {
406
- Authorization: `Bearer ${token}`,
407
- "Content-Type": "application/json"
408
- };
409
- }
410
- async handleRequest(request) {
411
- try {
412
- const res = await request;
413
- if (!res.ok) {
414
- let errorMessage = `Request failed with status ${res.status}`;
415
- let errorData;
416
- try {
417
- const json2 = await res.json();
418
- errorData = json2;
419
- if (json2.error || json2.message) {
420
- const errorDetails = typeof json2.error === "object" ? JSON.stringify(json2.error) : json2.error;
421
- const messageDetails = typeof json2.message === "object" ? JSON.stringify(json2.message) : json2.message;
422
- errorMessage = `${res.status} ${res.statusText}: ${messageDetails || errorDetails || "Unknown error"}`;
423
- }
424
- } catch (e) {
425
- console.log("Failed to parse error response:", e);
426
- errorMessage = `${res.status} ${res.statusText}`;
427
- }
428
- throw new DBError(
429
- errorMessage,
430
- res.status,
431
- errorData
432
- );
433
- }
434
- const json = await res.json();
435
- return json.data;
436
- } catch (error) {
437
- console.log("Caught error:", error);
438
- if (error instanceof Error) {
439
- console.log("Error type:", error.constructor.name);
440
- console.log("Error stack:", error.stack);
441
- }
442
- if (error instanceof DBError) {
443
- throw error;
444
- }
445
- if (error instanceof TypeError && error.message === "Network request failed") {
446
- throw new DBError(
447
- "Network request failed. Please check your internet connection and try again.",
448
- void 0,
449
- void 0,
450
- error
451
- );
452
- }
453
- throw new DBError(
454
- `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
455
- void 0,
456
- void 0,
457
- error instanceof Error ? error : void 0
458
- );
459
- }
460
- }
461
- // Build query string from query options
462
- buildQueryParams(query) {
463
- if (!query)
464
- return "";
465
- const params = [];
466
- if (query.id) {
467
- params.push(`id=${query.id}`);
468
- }
469
- if (query.filters) {
470
- for (const [field, condition] of Object.entries(query.filters)) {
471
- this.addFilterParam(params, field, condition);
472
- }
473
- }
474
- if (query.order) {
475
- params.push(`order=${query.order}`);
476
- }
477
- if (query.limit !== void 0 && query.limit >= 0) {
478
- params.push(`limit=${query.limit}`);
479
- }
480
- if (query.offset !== void 0 && query.offset >= 0) {
481
- params.push(`offset=${query.offset}`);
482
- }
483
- return params.length > 0 ? `?${params.join("&")}` : "";
484
- }
485
- // Helper method to build filter parameters
486
- addFilterParam(params, field, condition, negate = false) {
487
- if (condition === null || typeof condition !== "object") {
488
- if (condition === null) {
489
- params.push(`${field}=${negate ? "not." : ""}is.null`);
490
- } else if (typeof condition === "boolean") {
491
- params.push(`${field}=${negate ? "not." : ""}is.${condition}`);
492
- } else if (typeof condition === "number") {
493
- params.push(`${field}=${negate ? "not." : ""}eq.${condition}`);
494
- } else {
495
- params.push(`${field}=${negate ? "not." : ""}eq.${encodeURIComponent(String(condition))}`);
496
- }
497
- return;
498
- }
499
- const operatorObj = condition;
500
- if (operatorObj.not) {
501
- this.addFilterParam(params, field, operatorObj.not, true);
502
- return;
503
- }
504
- for (const [op, value] of Object.entries(operatorObj)) {
505
- if (op === "not")
506
- continue;
507
- const operator = op;
508
- if (value === null) {
509
- params.push(`${field}=${negate ? "not." : ""}is.null`);
510
- } else if (operator === "in" && Array.isArray(value)) {
511
- params.push(`${field}=${negate ? "not." : ""}in.${value.join(",")}`);
512
- } else if (operator === "is") {
513
- if (typeof value === "boolean") {
514
- params.push(`${field}=${negate ? "not." : ""}is.${value}`);
515
- } else {
516
- params.push(`${field}=${negate ? "not." : ""}is.null`);
517
- }
518
- } else {
519
- const paramValue = typeof value === "string" ? encodeURIComponent(value) : String(value);
520
- params.push(`${field}=${negate ? "not." : ""}${operator}.${paramValue}`);
521
- }
522
- }
523
- }
524
- // Internal method to execute a query with options
525
- async executeQuery(options) {
526
- const params = this.buildQueryParams(options);
527
- const headers = await this.headers();
528
- return this.handleRequest(
529
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}${params}`, {
530
- headers
531
- })
532
- );
533
- }
534
- // Public method to start building a query
535
- getAll() {
536
- return new QueryBuilder(this, this.tableSchema);
537
- }
538
- // Get a specific item by ID
539
- async get(id) {
540
- const headers = await this.headers();
541
- return this.handleRequest(
542
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
543
- headers
544
- })
545
- );
546
- }
547
- async create(value) {
548
- const headers = await this.headers();
549
- return this.handleRequest(
550
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}`, {
551
- method: "POST",
552
- headers,
553
- body: JSON.stringify({ value })
554
- })
555
- );
556
- }
557
- async update(id, value) {
558
- const headers = await this.headers();
559
- return this.handleRequest(
560
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
561
- method: "PATCH",
562
- headers,
563
- body: JSON.stringify({ value })
564
- })
565
- );
566
- }
567
- async replace(id, value) {
568
- const headers = await this.headers();
569
- return this.handleRequest(
570
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
571
- method: "PUT",
572
- headers,
573
- body: JSON.stringify({ value })
574
- })
575
- );
576
- }
577
- async delete(id) {
578
- const token = await this.getToken();
579
- const headers = {
580
- Authorization: `Bearer ${token}`
581
- };
582
- return this.handleRequest(
583
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
584
- method: "DELETE",
585
- headers
586
- })
587
- );
588
- }
589
- };
590
- var BasicDBSDK = class {
591
- projectId;
592
- getToken;
593
- baseUrl;
594
- schema;
595
- tableNames;
596
- constructor(config) {
597
- this.projectId = config.project_id;
598
- if (config.getToken) {
599
- this.getToken = config.getToken;
600
- } else if (config.token) {
601
- this.getToken = async () => config.token;
602
- } else {
603
- throw new Error("Either token or getToken must be provided");
604
- }
605
- this.baseUrl = config.baseUrl || "https://api.basic.tech";
606
- this.schema = config.schema;
607
- this.tableNames = Object.keys(this.schema.tables);
608
- }
609
- // Primary method - table access
610
- table(name) {
611
- if (!this.tableNames.includes(name)) {
612
- throw new Error(`Table '${name}' not found in schema. Available tables: ${this.tableNames.join(", ")}`);
613
- }
614
- return new TableClient(
615
- this.baseUrl,
616
- this.projectId,
617
- "",
618
- // Empty placeholder, will be replaced in headers() method
619
- name,
620
- this.getToken,
621
- this.schema
622
- // Pass the entire schema to the TableClient
623
- );
624
- }
625
- get tables() {
626
- return {};
627
- }
628
- fields(table) {
629
- const tableSchema = this.schema.tables[table];
630
- if (!tableSchema) {
631
- throw new Error(`Table '${table}' not found in schema`);
632
- }
633
- return Object.keys(tableSchema.fields);
634
- }
635
- };
636
-
637
281
  // package.json
638
- var version = "0.7.0-beta.1";
282
+ var version = "0.7.0-beta.2";
639
283
 
640
284
  // src/updater/versionUpdater.ts
641
285
  var VersionUpdater = class {
@@ -988,7 +632,6 @@ var BasicContext = (0, import_react.createContext)({
988
632
  }),
989
633
  getSignInLink: () => Promise.resolve(""),
990
634
  db: {},
991
- remoteDb: {},
992
635
  dbStatus: "LOADING" /* LOADING */
993
636
  });
994
637
  function BasicProvider({
@@ -1009,7 +652,6 @@ function BasicProvider({
1009
652
  const [isOnline, setIsOnline] = (0, import_react.useState)(navigator.onLine);
1010
653
  const [pendingRefresh, setPendingRefresh] = (0, import_react.useState)(false);
1011
654
  const syncRef = (0, import_react.useRef)(null);
1012
- const remoteDbRef = (0, import_react.useRef)(null);
1013
655
  const storageAdapter = storage || new LocalStorageAdapter();
1014
656
  const isDevMode = () => isDevelopment(debug);
1015
657
  const cleanOAuthParams = () => cleanOAuthParamsFromUrl();
@@ -1023,7 +665,7 @@ function BasicProvider({
1023
665
  if (token) {
1024
666
  const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
1025
667
  if (refreshToken) {
1026
- fetchToken(refreshToken).catch((error2) => {
668
+ fetchToken(refreshToken, true).catch((error2) => {
1027
669
  log("Retry refresh failed:", error2);
1028
670
  });
1029
671
  }
@@ -1105,17 +747,6 @@ function BasicProvider({
1105
747
  }
1106
748
  connectToDb();
1107
749
  }, [isSignedIn, shouldConnect]);
1108
- (0, import_react.useEffect)(() => {
1109
- if (project_id && schema && token?.access_token && !remoteDbRef.current) {
1110
- log("Initializing Remote DB SDK");
1111
- remoteDbRef.current = new BasicDBSDK({
1112
- project_id,
1113
- schema,
1114
- getToken: () => getToken(),
1115
- baseUrl: "https://api.basic.tech"
1116
- });
1117
- }
1118
- }, [token, project_id, schema]);
1119
750
  (0, import_react.useEffect)(() => {
1120
751
  const initializeAuth = async () => {
1121
752
  await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
@@ -1146,14 +777,14 @@ function BasicProvider({
1146
777
  }
1147
778
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
1148
779
  cleanOAuthParams();
1149
- fetchToken(code).catch((error2) => {
780
+ fetchToken(code, false).catch((error2) => {
1150
781
  log("Error fetching token:", error2);
1151
782
  });
1152
783
  } else {
1153
784
  const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
1154
785
  if (refreshToken) {
1155
786
  log("Found refresh token in storage, attempting to refresh access token");
1156
- fetchToken(refreshToken).catch((error2) => {
787
+ fetchToken(refreshToken, true).catch((error2) => {
1157
788
  log("Error fetching refresh token:", error2);
1158
789
  });
1159
790
  } else {
@@ -1222,7 +853,7 @@ function BasicProvider({
1222
853
  if (isExpired) {
1223
854
  log("token is expired - refreshing ...");
1224
855
  try {
1225
- const newToken = await fetchToken(token?.refresh_token || "");
856
+ const newToken = await fetchToken(token?.refresh_token || "", true);
1226
857
  fetchUser(newToken?.access_token || "");
1227
858
  } catch (error2) {
1228
859
  log("Failed to refresh token in checkToken:", error2);
@@ -1307,7 +938,7 @@ function BasicProvider({
1307
938
  }
1308
939
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
1309
940
  cleanOAuthParams();
1310
- const token2 = await fetchToken(code);
941
+ const token2 = await fetchToken(code, false);
1311
942
  if (token2) {
1312
943
  log("signinWithCode successful");
1313
944
  return { success: true };
@@ -1352,7 +983,7 @@ function BasicProvider({
1352
983
  if (refreshToken) {
1353
984
  log("No token in memory, attempting to refresh from storage");
1354
985
  try {
1355
- const newToken = await fetchToken(refreshToken);
986
+ const newToken = await fetchToken(refreshToken, true);
1356
987
  if (newToken?.access_token) {
1357
988
  return newToken.access_token;
1358
989
  }
@@ -1379,7 +1010,7 @@ function BasicProvider({
1379
1010
  const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
1380
1011
  if (refreshToken) {
1381
1012
  try {
1382
- const newToken = await fetchToken(refreshToken);
1013
+ const newToken = await fetchToken(refreshToken, true);
1383
1014
  return newToken?.access_token || "";
1384
1015
  } catch (error2) {
1385
1016
  log("Failed to refresh expired token:", error2);
@@ -1395,19 +1026,26 @@ function BasicProvider({
1395
1026
  }
1396
1027
  return token?.access_token || "";
1397
1028
  };
1398
- const fetchToken = async (code) => {
1029
+ const fetchToken = async (codeOrRefreshToken, isRefreshToken = false) => {
1399
1030
  try {
1400
1031
  if (!isOnline) {
1401
1032
  log("Network is offline, marking refresh as pending");
1402
1033
  setPendingRefresh(true);
1403
1034
  throw new Error("Network offline - refresh will be retried when online");
1404
1035
  }
1036
+ const requestBody = isRefreshToken ? {
1037
+ grant_type: "refresh_token",
1038
+ refresh_token: codeOrRefreshToken
1039
+ } : {
1040
+ grant_type: "authorization_code",
1041
+ code: codeOrRefreshToken
1042
+ };
1405
1043
  const token2 = await fetch("https://api.basic.tech/auth/token", {
1406
1044
  method: "POST",
1407
1045
  headers: {
1408
1046
  "Content-Type": "application/json"
1409
1047
  },
1410
- body: JSON.stringify({ code })
1048
+ body: JSON.stringify(requestBody)
1411
1049
  }).then((response) => response.json()).catch((error2) => {
1412
1050
  log("Network error fetching token:", error2);
1413
1051
  if (!isOnline) {
@@ -1473,7 +1111,6 @@ function BasicProvider({
1473
1111
  getToken,
1474
1112
  getSignInLink,
1475
1113
  db: syncRef.current ? syncRef.current : noDb,
1476
- remoteDb: remoteDbRef.current ? remoteDbRef.current : noDb,
1477
1114
  dbStatus
1478
1115
  }, children: [
1479
1116
  error && isDevMode() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorDisplay, { error }),
@@ -1511,7 +1148,6 @@ function useBasic() {
1511
1148
  var import_dexie_react_hooks = require("dexie-react-hooks");
1512
1149
  // Annotate the CommonJS export names for ESM import in node:
1513
1150
  0 && (module.exports = {
1514
- BasicDBSDK,
1515
1151
  BasicProvider,
1516
1152
  useBasic,
1517
1153
  useQuery