@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.mjs CHANGED
@@ -250,363 +250,8 @@ var BasicSync = class extends Dexie2 {
250
250
  }
251
251
  };
252
252
 
253
- // src/db_ts.ts
254
- var DBError = class extends Error {
255
- constructor(message, status, response, originalError) {
256
- super(message);
257
- this.status = status;
258
- this.response = response;
259
- this.originalError = originalError;
260
- this.name = "DBError";
261
- }
262
- };
263
- var QueryBuilder = class {
264
- constructor(tableClient, tableSchema) {
265
- this.tableClient = tableClient;
266
- this.tableSchema = tableSchema;
267
- }
268
- params = {};
269
- // Reserved fields that are always allowed
270
- reservedFields = ["created_at", "updated_at", "id"];
271
- // Validate field existence in schema
272
- validateField(field) {
273
- if (this.tableSchema && !this.reservedFields.includes(field)) {
274
- if (!this.tableSchema.fields || !(field in this.tableSchema.fields)) {
275
- throw new Error(`Invalid field: "${field}". Field does not exist in table schema.`);
276
- }
277
- }
278
- }
279
- // Validate operator based on field type
280
- validateOperator(field, operator, value) {
281
- if (!this.tableSchema || this.reservedFields.includes(field)) {
282
- return;
283
- }
284
- const fieldInfo = this.tableSchema.fields[field];
285
- if (!fieldInfo)
286
- return;
287
- switch (operator) {
288
- case "gt":
289
- case "gte":
290
- case "lt":
291
- case "lte":
292
- if (fieldInfo.type !== "number" && fieldInfo.type !== "string") {
293
- throw new Error(`Operator "${operator}" can only be used with number or string fields. Field "${field}" is type "${fieldInfo.type}".`);
294
- }
295
- break;
296
- case "like":
297
- case "ilike":
298
- if (fieldInfo.type !== "string") {
299
- throw new Error(`Operator "${operator}" can only be used with string fields. Field "${field}" is type "${fieldInfo.type}".`);
300
- }
301
- if (typeof value !== "string") {
302
- throw new Error(`Operator "${operator}" requires a string value. Received: ${typeof value}`);
303
- }
304
- break;
305
- case "in":
306
- if (!Array.isArray(value)) {
307
- throw new Error(`Operator "in" requires an array value. Received: ${typeof value}`);
308
- }
309
- break;
310
- case "is":
311
- if (value !== null && typeof value !== "boolean") {
312
- throw new Error(`Operator "is" requires null or boolean. Received: ${typeof value}`);
313
- }
314
- break;
315
- }
316
- }
317
- // Add ordering to query with schema validation
318
- order(field, direction = "asc") {
319
- this.validateField(field);
320
- this.params.order = `${field}.${direction}`;
321
- return this;
322
- }
323
- // Add filtering to query
324
- filter(conditions) {
325
- if (!this.params.filters) {
326
- this.params.filters = {};
327
- }
328
- for (const [field, condition] of Object.entries(conditions)) {
329
- this.validateField(field);
330
- if (condition === null || typeof condition !== "object") {
331
- this.params.filters[field] = condition;
332
- } else {
333
- this.params.filters[field] = condition;
334
- }
335
- }
336
- return this;
337
- }
338
- // Add limit to query
339
- limit(count) {
340
- this.params.limit = count;
341
- return this;
342
- }
343
- // Add offset to query for pagination
344
- offset(count) {
345
- this.params.offset = count;
346
- return this;
347
- }
348
- // Auto-execute when awaited
349
- then(onfulfilled, onrejected) {
350
- return this.tableClient.executeQuery(this.params).then(onfulfilled, onrejected);
351
- }
352
- // Auto-execute when awaited with catch
353
- catch(onrejected) {
354
- return this.tableClient.executeQuery(this.params).catch(onrejected);
355
- }
356
- // Auto-execute when awaited with finally
357
- finally(onfinally) {
358
- return this.tableClient.executeQuery(this.params).finally(onfinally);
359
- }
360
- };
361
- var TableClient = class {
362
- constructor(baseUrl, projectId, token, table, getToken, schema) {
363
- this.baseUrl = baseUrl;
364
- this.projectId = projectId;
365
- this.token = token;
366
- this.table = table;
367
- this.getToken = getToken;
368
- this.schema = schema;
369
- if (schema && schema.tables && schema.tables[table]) {
370
- this.tableSchema = schema.tables[table];
371
- }
372
- }
373
- tableSchema;
374
- async headers() {
375
- const token = await this.getToken();
376
- return {
377
- Authorization: `Bearer ${token}`,
378
- "Content-Type": "application/json"
379
- };
380
- }
381
- async handleRequest(request) {
382
- try {
383
- const res = await request;
384
- if (!res.ok) {
385
- let errorMessage = `Request failed with status ${res.status}`;
386
- let errorData;
387
- try {
388
- const json2 = await res.json();
389
- errorData = json2;
390
- if (json2.error || json2.message) {
391
- const errorDetails = typeof json2.error === "object" ? JSON.stringify(json2.error) : json2.error;
392
- const messageDetails = typeof json2.message === "object" ? JSON.stringify(json2.message) : json2.message;
393
- errorMessage = `${res.status} ${res.statusText}: ${messageDetails || errorDetails || "Unknown error"}`;
394
- }
395
- } catch (e) {
396
- console.log("Failed to parse error response:", e);
397
- errorMessage = `${res.status} ${res.statusText}`;
398
- }
399
- throw new DBError(
400
- errorMessage,
401
- res.status,
402
- errorData
403
- );
404
- }
405
- const json = await res.json();
406
- return json.data;
407
- } catch (error) {
408
- console.log("Caught error:", error);
409
- if (error instanceof Error) {
410
- console.log("Error type:", error.constructor.name);
411
- console.log("Error stack:", error.stack);
412
- }
413
- if (error instanceof DBError) {
414
- throw error;
415
- }
416
- if (error instanceof TypeError && error.message === "Network request failed") {
417
- throw new DBError(
418
- "Network request failed. Please check your internet connection and try again.",
419
- void 0,
420
- void 0,
421
- error
422
- );
423
- }
424
- throw new DBError(
425
- `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
426
- void 0,
427
- void 0,
428
- error instanceof Error ? error : void 0
429
- );
430
- }
431
- }
432
- // Build query string from query options
433
- buildQueryParams(query) {
434
- if (!query)
435
- return "";
436
- const params = [];
437
- if (query.id) {
438
- params.push(`id=${query.id}`);
439
- }
440
- if (query.filters) {
441
- for (const [field, condition] of Object.entries(query.filters)) {
442
- this.addFilterParam(params, field, condition);
443
- }
444
- }
445
- if (query.order) {
446
- params.push(`order=${query.order}`);
447
- }
448
- if (query.limit !== void 0 && query.limit >= 0) {
449
- params.push(`limit=${query.limit}`);
450
- }
451
- if (query.offset !== void 0 && query.offset >= 0) {
452
- params.push(`offset=${query.offset}`);
453
- }
454
- return params.length > 0 ? `?${params.join("&")}` : "";
455
- }
456
- // Helper method to build filter parameters
457
- addFilterParam(params, field, condition, negate = false) {
458
- if (condition === null || typeof condition !== "object") {
459
- if (condition === null) {
460
- params.push(`${field}=${negate ? "not." : ""}is.null`);
461
- } else if (typeof condition === "boolean") {
462
- params.push(`${field}=${negate ? "not." : ""}is.${condition}`);
463
- } else if (typeof condition === "number") {
464
- params.push(`${field}=${negate ? "not." : ""}eq.${condition}`);
465
- } else {
466
- params.push(`${field}=${negate ? "not." : ""}eq.${encodeURIComponent(String(condition))}`);
467
- }
468
- return;
469
- }
470
- const operatorObj = condition;
471
- if (operatorObj.not) {
472
- this.addFilterParam(params, field, operatorObj.not, true);
473
- return;
474
- }
475
- for (const [op, value] of Object.entries(operatorObj)) {
476
- if (op === "not")
477
- continue;
478
- const operator = op;
479
- if (value === null) {
480
- params.push(`${field}=${negate ? "not." : ""}is.null`);
481
- } else if (operator === "in" && Array.isArray(value)) {
482
- params.push(`${field}=${negate ? "not." : ""}in.${value.join(",")}`);
483
- } else if (operator === "is") {
484
- if (typeof value === "boolean") {
485
- params.push(`${field}=${negate ? "not." : ""}is.${value}`);
486
- } else {
487
- params.push(`${field}=${negate ? "not." : ""}is.null`);
488
- }
489
- } else {
490
- const paramValue = typeof value === "string" ? encodeURIComponent(value) : String(value);
491
- params.push(`${field}=${negate ? "not." : ""}${operator}.${paramValue}`);
492
- }
493
- }
494
- }
495
- // Internal method to execute a query with options
496
- async executeQuery(options) {
497
- const params = this.buildQueryParams(options);
498
- const headers = await this.headers();
499
- return this.handleRequest(
500
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}${params}`, {
501
- headers
502
- })
503
- );
504
- }
505
- // Public method to start building a query
506
- getAll() {
507
- return new QueryBuilder(this, this.tableSchema);
508
- }
509
- // Get a specific item by ID
510
- async get(id) {
511
- const headers = await this.headers();
512
- return this.handleRequest(
513
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
514
- headers
515
- })
516
- );
517
- }
518
- async create(value) {
519
- const headers = await this.headers();
520
- return this.handleRequest(
521
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}`, {
522
- method: "POST",
523
- headers,
524
- body: JSON.stringify({ value })
525
- })
526
- );
527
- }
528
- async update(id, value) {
529
- const headers = await this.headers();
530
- return this.handleRequest(
531
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
532
- method: "PATCH",
533
- headers,
534
- body: JSON.stringify({ value })
535
- })
536
- );
537
- }
538
- async replace(id, value) {
539
- const headers = await this.headers();
540
- return this.handleRequest(
541
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
542
- method: "PUT",
543
- headers,
544
- body: JSON.stringify({ value })
545
- })
546
- );
547
- }
548
- async delete(id) {
549
- const token = await this.getToken();
550
- const headers = {
551
- Authorization: `Bearer ${token}`
552
- };
553
- return this.handleRequest(
554
- fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
555
- method: "DELETE",
556
- headers
557
- })
558
- );
559
- }
560
- };
561
- var BasicDBSDK = class {
562
- projectId;
563
- getToken;
564
- baseUrl;
565
- schema;
566
- tableNames;
567
- constructor(config) {
568
- this.projectId = config.project_id;
569
- if (config.getToken) {
570
- this.getToken = config.getToken;
571
- } else if (config.token) {
572
- this.getToken = async () => config.token;
573
- } else {
574
- throw new Error("Either token or getToken must be provided");
575
- }
576
- this.baseUrl = config.baseUrl || "https://api.basic.tech";
577
- this.schema = config.schema;
578
- this.tableNames = Object.keys(this.schema.tables);
579
- }
580
- // Primary method - table access
581
- table(name) {
582
- if (!this.tableNames.includes(name)) {
583
- throw new Error(`Table '${name}' not found in schema. Available tables: ${this.tableNames.join(", ")}`);
584
- }
585
- return new TableClient(
586
- this.baseUrl,
587
- this.projectId,
588
- "",
589
- // Empty placeholder, will be replaced in headers() method
590
- name,
591
- this.getToken,
592
- this.schema
593
- // Pass the entire schema to the TableClient
594
- );
595
- }
596
- get tables() {
597
- return {};
598
- }
599
- fields(table) {
600
- const tableSchema = this.schema.tables[table];
601
- if (!tableSchema) {
602
- throw new Error(`Table '${table}' not found in schema`);
603
- }
604
- return Object.keys(tableSchema.fields);
605
- }
606
- };
607
-
608
253
  // package.json
609
- var version = "0.7.0-beta.1";
254
+ var version = "0.7.0-beta.2";
610
255
 
611
256
  // src/updater/versionUpdater.ts
612
257
  var VersionUpdater = class {
@@ -959,7 +604,6 @@ var BasicContext = createContext({
959
604
  }),
960
605
  getSignInLink: () => Promise.resolve(""),
961
606
  db: {},
962
- remoteDb: {},
963
607
  dbStatus: "LOADING" /* LOADING */
964
608
  });
965
609
  function BasicProvider({
@@ -980,7 +624,6 @@ function BasicProvider({
980
624
  const [isOnline, setIsOnline] = useState(navigator.onLine);
981
625
  const [pendingRefresh, setPendingRefresh] = useState(false);
982
626
  const syncRef = useRef(null);
983
- const remoteDbRef = useRef(null);
984
627
  const storageAdapter = storage || new LocalStorageAdapter();
985
628
  const isDevMode = () => isDevelopment(debug);
986
629
  const cleanOAuthParams = () => cleanOAuthParamsFromUrl();
@@ -994,7 +637,7 @@ function BasicProvider({
994
637
  if (token) {
995
638
  const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
996
639
  if (refreshToken) {
997
- fetchToken(refreshToken).catch((error2) => {
640
+ fetchToken(refreshToken, true).catch((error2) => {
998
641
  log("Retry refresh failed:", error2);
999
642
  });
1000
643
  }
@@ -1076,17 +719,6 @@ function BasicProvider({
1076
719
  }
1077
720
  connectToDb();
1078
721
  }, [isSignedIn, shouldConnect]);
1079
- useEffect(() => {
1080
- if (project_id && schema && token?.access_token && !remoteDbRef.current) {
1081
- log("Initializing Remote DB SDK");
1082
- remoteDbRef.current = new BasicDBSDK({
1083
- project_id,
1084
- schema,
1085
- getToken: () => getToken(),
1086
- baseUrl: "https://api.basic.tech"
1087
- });
1088
- }
1089
- }, [token, project_id, schema]);
1090
722
  useEffect(() => {
1091
723
  const initializeAuth = async () => {
1092
724
  await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
@@ -1117,14 +749,14 @@ function BasicProvider({
1117
749
  }
1118
750
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
1119
751
  cleanOAuthParams();
1120
- fetchToken(code).catch((error2) => {
752
+ fetchToken(code, false).catch((error2) => {
1121
753
  log("Error fetching token:", error2);
1122
754
  });
1123
755
  } else {
1124
756
  const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
1125
757
  if (refreshToken) {
1126
758
  log("Found refresh token in storage, attempting to refresh access token");
1127
- fetchToken(refreshToken).catch((error2) => {
759
+ fetchToken(refreshToken, true).catch((error2) => {
1128
760
  log("Error fetching refresh token:", error2);
1129
761
  });
1130
762
  } else {
@@ -1193,7 +825,7 @@ function BasicProvider({
1193
825
  if (isExpired) {
1194
826
  log("token is expired - refreshing ...");
1195
827
  try {
1196
- const newToken = await fetchToken(token?.refresh_token || "");
828
+ const newToken = await fetchToken(token?.refresh_token || "", true);
1197
829
  fetchUser(newToken?.access_token || "");
1198
830
  } catch (error2) {
1199
831
  log("Failed to refresh token in checkToken:", error2);
@@ -1278,7 +910,7 @@ function BasicProvider({
1278
910
  }
1279
911
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
1280
912
  cleanOAuthParams();
1281
- const token2 = await fetchToken(code);
913
+ const token2 = await fetchToken(code, false);
1282
914
  if (token2) {
1283
915
  log("signinWithCode successful");
1284
916
  return { success: true };
@@ -1323,7 +955,7 @@ function BasicProvider({
1323
955
  if (refreshToken) {
1324
956
  log("No token in memory, attempting to refresh from storage");
1325
957
  try {
1326
- const newToken = await fetchToken(refreshToken);
958
+ const newToken = await fetchToken(refreshToken, true);
1327
959
  if (newToken?.access_token) {
1328
960
  return newToken.access_token;
1329
961
  }
@@ -1350,7 +982,7 @@ function BasicProvider({
1350
982
  const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
1351
983
  if (refreshToken) {
1352
984
  try {
1353
- const newToken = await fetchToken(refreshToken);
985
+ const newToken = await fetchToken(refreshToken, true);
1354
986
  return newToken?.access_token || "";
1355
987
  } catch (error2) {
1356
988
  log("Failed to refresh expired token:", error2);
@@ -1366,19 +998,26 @@ function BasicProvider({
1366
998
  }
1367
999
  return token?.access_token || "";
1368
1000
  };
1369
- const fetchToken = async (code) => {
1001
+ const fetchToken = async (codeOrRefreshToken, isRefreshToken = false) => {
1370
1002
  try {
1371
1003
  if (!isOnline) {
1372
1004
  log("Network is offline, marking refresh as pending");
1373
1005
  setPendingRefresh(true);
1374
1006
  throw new Error("Network offline - refresh will be retried when online");
1375
1007
  }
1008
+ const requestBody = isRefreshToken ? {
1009
+ grant_type: "refresh_token",
1010
+ refresh_token: codeOrRefreshToken
1011
+ } : {
1012
+ grant_type: "authorization_code",
1013
+ code: codeOrRefreshToken
1014
+ };
1376
1015
  const token2 = await fetch("https://api.basic.tech/auth/token", {
1377
1016
  method: "POST",
1378
1017
  headers: {
1379
1018
  "Content-Type": "application/json"
1380
1019
  },
1381
- body: JSON.stringify({ code })
1020
+ body: JSON.stringify(requestBody)
1382
1021
  }).then((response) => response.json()).catch((error2) => {
1383
1022
  log("Network error fetching token:", error2);
1384
1023
  if (!isOnline) {
@@ -1444,7 +1083,6 @@ function BasicProvider({
1444
1083
  getToken,
1445
1084
  getSignInLink,
1446
1085
  db: syncRef.current ? syncRef.current : noDb,
1447
- remoteDb: remoteDbRef.current ? remoteDbRef.current : noDb,
1448
1086
  dbStatus
1449
1087
  }, children: [
1450
1088
  error && isDevMode() && /* @__PURE__ */ jsx(ErrorDisplay, { error }),
@@ -1481,7 +1119,6 @@ function useBasic() {
1481
1119
  // src/index.ts
1482
1120
  import { useLiveQuery as useQuery } from "dexie-react-hooks";
1483
1121
  export {
1484
- BasicDBSDK,
1485
1122
  BasicProvider,
1486
1123
  useBasic,
1487
1124
  useQuery