@basictech/react 0.7.0-beta.1 → 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
@@ -127,116 +127,8 @@ var syncProtocol = function() {
127
127
  });
128
128
  };
129
129
 
130
- // src/schema.ts
131
- import Ajv from "ajv";
132
- var basicJsonSchema = {
133
- "$schema": "http://json-schema.org/draft-07/schema#",
134
- "type": "object",
135
- "properties": {
136
- "project_id": {
137
- "type": "string"
138
- },
139
- "namespace": {
140
- "type": "string"
141
- },
142
- "version": {
143
- "type": "integer",
144
- "minimum": 0
145
- },
146
- "tables": {
147
- "type": "object",
148
- "patternProperties": {
149
- "^[a-zA-Z0-9_]+$": {
150
- "type": "object",
151
- "properties": {
152
- "name": {
153
- "type": "string"
154
- },
155
- "type": {
156
- "type": "string",
157
- "enum": ["collection"]
158
- },
159
- "fields": {
160
- "type": "object",
161
- "patternProperties": {
162
- "^[a-zA-Z0-9_]+$": {
163
- "type": "object",
164
- "properties": {
165
- "type": {
166
- "type": "string",
167
- "enum": ["string", "boolean", "number", "json"]
168
- },
169
- "indexed": {
170
- "type": "boolean"
171
- },
172
- "required": {
173
- "type": "boolean"
174
- }
175
- },
176
- "required": ["type"]
177
- }
178
- },
179
- "additionalProperties": true
180
- }
181
- },
182
- "required": ["fields"]
183
- }
184
- },
185
- "additionalProperties": true
186
- }
187
- },
188
- "required": ["project_id", "version", "tables"]
189
- };
190
- var ajv = new Ajv();
191
- var validator = ajv.compile(basicJsonSchema);
192
- function validateSchema(schema) {
193
- const v = validator(schema);
194
- return {
195
- valid: v,
196
- errors: validator.errors || []
197
- };
198
- }
199
- function validateData(schema, table, data, checkRequired = true) {
200
- const valid = validateSchema(schema);
201
- if (!valid.valid) {
202
- return { valid: false, errors: valid.errors, message: "Schema is invalid" };
203
- }
204
- const tableSchema = schema.tables[table];
205
- if (!tableSchema) {
206
- return { valid: false, errors: [{ message: `Table ${table} not found in schema` }], message: "Table not found" };
207
- }
208
- for (const [fieldName, fieldValue] of Object.entries(data)) {
209
- const fieldSchema = tableSchema.fields[fieldName];
210
- if (!fieldSchema) {
211
- return {
212
- valid: false,
213
- errors: [{ message: `Field ${fieldName} not found in schema` }],
214
- message: "Invalid field"
215
- };
216
- }
217
- const schemaType = fieldSchema.type;
218
- const valueType = typeof fieldValue;
219
- if (schemaType === "string" && valueType !== "string" || schemaType === "number" && valueType !== "number" || schemaType === "boolean" && valueType !== "boolean" || schemaType === "json" && valueType !== "object") {
220
- return {
221
- valid: false,
222
- errors: [{
223
- message: `Field ${fieldName} should be type ${schemaType}, got ${valueType}`
224
- }],
225
- message: "invalid type"
226
- };
227
- }
228
- }
229
- if (checkRequired) {
230
- for (const [fieldName, fieldSchema] of Object.entries(tableSchema.fields)) {
231
- if (fieldSchema.required && !data[fieldName]) {
232
- return { valid: false, errors: [{ message: `Field ${fieldName} is required` }], message: "Required field missing" };
233
- }
234
- }
235
- }
236
- return { valid: true, errors: [] };
237
- }
238
-
239
130
  // src/sync/index.ts
131
+ import { validateData } from "@basictech/schema";
240
132
  syncProtocol();
241
133
  var BasicSync = class extends Dexie2 {
242
134
  basic_schema;
@@ -358,60 +250,135 @@ var BasicSync = class extends Dexie2 {
358
250
  }
359
251
  };
360
252
 
361
- // src/db.ts
362
- var baseUrl = "https://api.basic.tech";
363
- async function get({ projectId, accountId, tableName, token }) {
364
- const url = `${baseUrl}/project/${projectId}/db/${accountId}/${tableName}`;
365
- const response = await fetch(url, {
366
- headers: {
367
- "Authorization": `Bearer ${token}`
253
+ // package.json
254
+ var version = "0.7.0-beta.2";
255
+
256
+ // src/updater/versionUpdater.ts
257
+ var VersionUpdater = class {
258
+ storage;
259
+ currentVersion;
260
+ migrations;
261
+ versionKey = "basic_app_version";
262
+ constructor(storage, currentVersion, migrations = []) {
263
+ this.storage = storage;
264
+ this.currentVersion = currentVersion;
265
+ this.migrations = migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion));
266
+ }
267
+ /**
268
+ * Check current stored version and run migrations if needed
269
+ * Only compares major.minor versions, ignoring beta/prerelease parts
270
+ * Example: "0.7.0-beta.1" and "0.7.0" are treated as the same version
271
+ */
272
+ async checkAndUpdate() {
273
+ const storedVersion = await this.getStoredVersion();
274
+ if (!storedVersion) {
275
+ await this.setStoredVersion(this.currentVersion);
276
+ return { updated: false, toVersion: this.currentVersion };
368
277
  }
369
- });
370
- return response.json();
371
- }
372
- async function add({ projectId, accountId, tableName, value, token }) {
373
- const url = `${baseUrl}/project/${projectId}/db/${accountId}/${tableName}`;
374
- const response = await fetch(url, {
375
- method: "POST",
376
- headers: {
377
- "Content-Type": "application/json",
378
- "Authorization": `Bearer ${token}`
379
- },
380
- body: JSON.stringify({ "value": value })
381
- });
382
- return response.json();
383
- }
384
- async function update({ projectId, accountId, tableName, id, value, token }) {
385
- const url = `${baseUrl}/project/${projectId}/db/${accountId}/${tableName}/${id}`;
386
- const response = await fetch(url, {
387
- method: "PATCH",
388
- headers: {
389
- "Content-Type": "application/json",
390
- "Authorization": `Bearer ${token}`
391
- },
392
- body: JSON.stringify({ id, value })
393
- });
394
- return response.json();
395
- }
396
- async function deleteRecord({ projectId, accountId, tableName, id, token }) {
397
- const url = `${baseUrl}/project/${projectId}/db/${accountId}/${tableName}/${id}`;
398
- const response = await fetch(url, {
399
- method: "DELETE",
400
- headers: {
401
- "Authorization": `Bearer ${token}`
278
+ if (storedVersion === this.currentVersion) {
279
+ return { updated: false, toVersion: this.currentVersion };
402
280
  }
403
- });
404
- return response.json();
281
+ const migrationsToRun = this.getMigrationsToRun(storedVersion, this.currentVersion);
282
+ if (migrationsToRun.length === 0) {
283
+ await this.setStoredVersion(this.currentVersion);
284
+ return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion };
285
+ }
286
+ for (const migration of migrationsToRun) {
287
+ try {
288
+ console.log(`Running migration from ${migration.fromVersion} to ${migration.toVersion}`);
289
+ await migration.migrate(this.storage);
290
+ } catch (error) {
291
+ console.error(`Migration failed from ${migration.fromVersion} to ${migration.toVersion}:`, error);
292
+ throw new Error(`Migration failed: ${error}`);
293
+ }
294
+ }
295
+ await this.setStoredVersion(this.currentVersion);
296
+ return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion };
297
+ }
298
+ async getStoredVersion() {
299
+ try {
300
+ const versionData = await this.storage.get(this.versionKey);
301
+ if (!versionData)
302
+ return null;
303
+ const versionInfo = JSON.parse(versionData);
304
+ return versionInfo.version;
305
+ } catch (error) {
306
+ console.warn("Failed to get stored version:", error);
307
+ return null;
308
+ }
309
+ }
310
+ async setStoredVersion(version2) {
311
+ const versionInfo = {
312
+ version: version2,
313
+ lastUpdated: Date.now()
314
+ };
315
+ await this.storage.set(this.versionKey, JSON.stringify(versionInfo));
316
+ }
317
+ getMigrationsToRun(fromVersion, toVersion) {
318
+ return this.migrations.filter((migration) => {
319
+ const storedLessThanMigrationTo = this.compareVersions(fromVersion, migration.toVersion) < 0;
320
+ const currentGreaterThanOrEqualMigrationTo = this.compareVersions(toVersion, migration.toVersion) >= 0;
321
+ console.log(`Checking migration ${migration.fromVersion} \u2192 ${migration.toVersion}:`);
322
+ console.log(` stored ${fromVersion} < migration.to ${migration.toVersion}: ${storedLessThanMigrationTo}`);
323
+ console.log(` current ${toVersion} >= migration.to ${migration.toVersion}: ${currentGreaterThanOrEqualMigrationTo}`);
324
+ const shouldRun = storedLessThanMigrationTo && currentGreaterThanOrEqualMigrationTo;
325
+ console.log(` Should run: ${shouldRun}`);
326
+ return shouldRun;
327
+ });
328
+ }
329
+ /**
330
+ * Simple semantic version comparison (major.minor only, ignoring beta/prerelease)
331
+ * Returns: -1 if a < b, 0 if a === b, 1 if a > b
332
+ */
333
+ compareVersions(a, b) {
334
+ const aMajorMinor = this.extractMajorMinor(a);
335
+ const bMajorMinor = this.extractMajorMinor(b);
336
+ if (aMajorMinor.major !== bMajorMinor.major) {
337
+ return aMajorMinor.major - bMajorMinor.major;
338
+ }
339
+ return aMajorMinor.minor - bMajorMinor.minor;
340
+ }
341
+ /**
342
+ * Extract major.minor from version string, ignoring beta/prerelease
343
+ * Examples: "0.7.0-beta.1" -> {major: 0, minor: 7}
344
+ * "1.2.3" -> {major: 1, minor: 2}
345
+ */
346
+ extractMajorMinor(version2) {
347
+ const cleanVersion = version2.split("-")[0]?.split("+")[0] || version2;
348
+ const parts = cleanVersion.split(".").map(Number);
349
+ return {
350
+ major: parts[0] || 0,
351
+ minor: parts[1] || 0
352
+ };
353
+ }
354
+ /**
355
+ * Add a migration to the updater
356
+ */
357
+ addMigration(migration) {
358
+ this.migrations.push(migration);
359
+ this.migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion));
360
+ }
361
+ };
362
+ function createVersionUpdater(storage, currentVersion, migrations = []) {
363
+ return new VersionUpdater(storage, currentVersion, migrations);
405
364
  }
406
365
 
407
- // src/AuthContext.tsx
408
- import { validateSchema as validateSchema3, compareSchemas } from "@basictech/schema";
409
-
410
- // package.json
411
- var version = "0.6.0";
366
+ // src/updater/updateMigrations.ts
367
+ var addMigrationTimestamp = {
368
+ fromVersion: "0.6.0",
369
+ toVersion: "0.7.0",
370
+ async migrate(storage) {
371
+ console.log("Running test migration");
372
+ storage.set("test_migration", "true");
373
+ }
374
+ };
375
+ function getMigrations() {
376
+ return [
377
+ addMigrationTimestamp
378
+ ];
379
+ }
412
380
 
413
- // src/AuthContext.tsx
414
- import { jsx, jsxs } from "react/jsx-runtime";
381
+ // src/utils/storage.ts
415
382
  var LocalStorageAdapter = class {
416
383
  async get(key) {
417
384
  return localStorage.getItem(key);
@@ -423,25 +390,113 @@ var LocalStorageAdapter = class {
423
390
  localStorage.removeItem(key);
424
391
  }
425
392
  };
426
- var BasicContext = createContext({
427
- unicorn: "\u{1F984}",
428
- isAuthReady: false,
429
- isSignedIn: false,
430
- user: null,
431
- signout: () => Promise.resolve(),
432
- signin: () => Promise.resolve(),
433
- signinWithCode: () => new Promise(() => {
434
- }),
435
- getToken: () => new Promise(() => {
436
- }),
437
- getSignInLink: () => Promise.resolve(""),
438
- db: {},
439
- dbStatus: "LOADING" /* LOADING */
440
- });
393
+ var STORAGE_KEYS = {
394
+ REFRESH_TOKEN: "basic_refresh_token",
395
+ USER_INFO: "basic_user_info",
396
+ AUTH_STATE: "basic_auth_state",
397
+ DEBUG: "basic_debug"
398
+ };
399
+ function getCookie(name) {
400
+ let cookieValue = "";
401
+ if (document.cookie && document.cookie !== "") {
402
+ const cookies = document.cookie.split(";");
403
+ for (let i = 0; i < cookies.length; i++) {
404
+ const cookie = cookies[i]?.trim();
405
+ if (cookie && cookie.substring(0, name.length + 1) === name + "=") {
406
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
407
+ break;
408
+ }
409
+ }
410
+ }
411
+ return cookieValue;
412
+ }
413
+ function setCookie(name, value, options) {
414
+ const opts = {
415
+ secure: true,
416
+ sameSite: "Strict",
417
+ httpOnly: false,
418
+ ...options
419
+ };
420
+ let cookieString = `${name}=${value}`;
421
+ if (opts.secure)
422
+ cookieString += "; Secure";
423
+ if (opts.sameSite)
424
+ cookieString += `; SameSite=${opts.sameSite}`;
425
+ if (opts.httpOnly)
426
+ cookieString += "; HttpOnly";
427
+ document.cookie = cookieString;
428
+ }
429
+ function clearCookie(name) {
430
+ document.cookie = `${name}=; Secure; SameSite=Strict`;
431
+ }
432
+
433
+ // src/utils/network.ts
434
+ function isDevelopment(debug) {
435
+ return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes("localhost") || window.location.hostname.includes("127.0.0.1") || window.location.hostname.includes(".local") || process.env.NODE_ENV === "development" || debug === true;
436
+ }
437
+ async function checkForNewVersion() {
438
+ try {
439
+ const isBeta = version.includes("beta");
440
+ const response = await fetch(`https://registry.npmjs.org/@basictech/react/${isBeta ? "beta" : "latest"}`);
441
+ if (!response.ok) {
442
+ throw new Error("Failed to fetch version from npm");
443
+ }
444
+ const data = await response.json();
445
+ const latestVersion = data.version;
446
+ if (latestVersion !== version) {
447
+ console.warn("[basic] New version available:", latestVersion, `
448
+ run "npm install @basictech/react@${latestVersion}" to update`);
449
+ }
450
+ if (isBeta) {
451
+ log("thank you for being on basictech/react beta :)");
452
+ }
453
+ return {
454
+ hasNewVersion: version !== latestVersion,
455
+ latestVersion,
456
+ currentVersion: version
457
+ };
458
+ } catch (error) {
459
+ log("Error checking for new version:", error);
460
+ return {
461
+ hasNewVersion: false,
462
+ latestVersion: null,
463
+ currentVersion: null
464
+ };
465
+ }
466
+ }
467
+ function cleanOAuthParamsFromUrl() {
468
+ if (window.location.search.includes("code") || window.location.search.includes("state")) {
469
+ const url = new URL(window.location.href);
470
+ url.searchParams.delete("code");
471
+ url.searchParams.delete("state");
472
+ window.history.pushState({}, document.title, url.pathname + url.search);
473
+ log("Cleaned OAuth parameters from URL");
474
+ }
475
+ }
476
+ function getSyncStatus(statusCode) {
477
+ switch (statusCode) {
478
+ case -1:
479
+ return "ERROR";
480
+ case 0:
481
+ return "OFFLINE";
482
+ case 1:
483
+ return "CONNECTING";
484
+ case 2:
485
+ return "ONLINE";
486
+ case 3:
487
+ return "SYNCING";
488
+ case 4:
489
+ return "ERROR_WILL_RETRY";
490
+ default:
491
+ return "UNKNOWN";
492
+ }
493
+ }
494
+
495
+ // src/utils/schema.ts
496
+ import { validateSchema as validateSchema2, compareSchemas } from "@basictech/schema";
441
497
  async function getSchemaStatus(schema) {
442
498
  const projectId = schema.project_id;
443
- let status = "";
444
- const valid = validateSchema3(schema);
499
+ const valid = validateSchema2(schema);
445
500
  if (!valid.valid) {
446
501
  console.warn("BasicDB Error: your local schema is invalid. Please fix errors and try again - sync is disabled");
447
502
  return {
@@ -503,54 +558,54 @@ async function getSchemaStatus(schema) {
503
558
  };
504
559
  }
505
560
  }
506
- function getSyncStatus(statusCode) {
507
- switch (statusCode) {
508
- case -1:
509
- return "ERROR";
510
- case 0:
511
- return "OFFLINE";
512
- case 1:
513
- return "CONNECTING";
514
- case 2:
515
- return "ONLINE";
516
- case 3:
517
- return "SYNCING";
518
- case 4:
519
- return "ERROR_WILL_RETRY";
520
- default:
521
- return "UNKNOWN";
522
- }
523
- }
524
- async function checkForNewVersion() {
525
- try {
526
- const isBeta = version.includes("beta");
527
- const response = await fetch(`https://registry.npmjs.org/@basictech/react/${isBeta ? "beta" : "latest"}`);
528
- if (!response.ok) {
529
- throw new Error("Failed to fetch version from npm");
530
- }
531
- const data = await response.json();
532
- const latestVersion = data.version;
533
- if (latestVersion !== version) {
534
- console.warn("[basic] New version available:", latestVersion, `
535
- run "npm install @basictech/react@${latestVersion}" to update`);
536
- }
537
- if (isBeta) {
538
- log("thank you for being on basictech/react beta :)");
539
- }
540
- return {
541
- hasNewVersion: version !== latestVersion,
542
- latestVersion,
543
- currentVersion: version
544
- };
545
- } catch (error) {
546
- log("Error checking for new version:", error);
561
+ async function validateAndCheckSchema(schema) {
562
+ const valid = validateSchema2(schema);
563
+ if (!valid.valid) {
564
+ log("Basic Schema is invalid!", valid.errors);
565
+ console.group("Schema Errors");
566
+ let errorMessage = "";
567
+ valid.errors.forEach((error, index) => {
568
+ log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`);
569
+ errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}
570
+ `;
571
+ });
572
+ console.groupEnd();
547
573
  return {
548
- hasNewVersion: false,
549
- latestVersion: null,
550
- currentVersion: null
574
+ isValid: false,
575
+ schemaStatus: { valid: false },
576
+ errors: valid.errors
551
577
  };
552
578
  }
579
+ let schemaStatus = { valid: false };
580
+ if (schema.version !== 0) {
581
+ schemaStatus = await getSchemaStatus(schema);
582
+ log("schemaStatus", schemaStatus);
583
+ } else {
584
+ log("schema not published - at version 0");
585
+ }
586
+ return {
587
+ isValid: true,
588
+ schemaStatus
589
+ };
553
590
  }
591
+
592
+ // src/AuthContext.tsx
593
+ import { jsx, jsxs } from "react/jsx-runtime";
594
+ var BasicContext = createContext({
595
+ unicorn: "\u{1F984}",
596
+ isAuthReady: false,
597
+ isSignedIn: false,
598
+ user: null,
599
+ signout: () => Promise.resolve(),
600
+ signin: () => Promise.resolve(),
601
+ signinWithCode: () => new Promise(() => {
602
+ }),
603
+ getToken: () => new Promise(() => {
604
+ }),
605
+ getSignInLink: () => Promise.resolve(""),
606
+ db: {},
607
+ dbStatus: "LOADING" /* LOADING */
608
+ });
554
609
  function BasicProvider({
555
610
  children,
556
611
  project_id,
@@ -570,24 +625,8 @@ function BasicProvider({
570
625
  const [pendingRefresh, setPendingRefresh] = useState(false);
571
626
  const syncRef = useRef(null);
572
627
  const storageAdapter = storage || new LocalStorageAdapter();
573
- const STORAGE_KEYS = {
574
- REFRESH_TOKEN: "basic_refresh_token",
575
- USER_INFO: "basic_user_info",
576
- AUTH_STATE: "basic_auth_state",
577
- DEBUG: "basic_debug"
578
- };
579
- const isDevelopment = () => {
580
- return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes("localhost") || window.location.hostname.includes("127.0.0.1") || window.location.hostname.includes(".local") || process.env.NODE_ENV === "development" || debug === true;
581
- };
582
- const cleanOAuthParamsFromUrl = () => {
583
- if (window.location.search.includes("code") || window.location.search.includes("state")) {
584
- const url = new URL(window.location.href);
585
- url.searchParams.delete("code");
586
- url.searchParams.delete("state");
587
- window.history.pushState({}, document.title, url.pathname + url.search);
588
- log("Cleaned OAuth parameters from URL");
589
- }
590
- };
628
+ const isDevMode = () => isDevelopment(debug);
629
+ const cleanOAuthParams = () => cleanOAuthParamsFromUrl();
591
630
  useEffect(() => {
592
631
  const handleOnline = () => {
593
632
  log("Network came back online");
@@ -598,7 +637,7 @@ function BasicProvider({
598
637
  if (token) {
599
638
  const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
600
639
  if (refreshToken) {
601
- fetchToken(refreshToken).catch((error2) => {
640
+ fetchToken(refreshToken, true).catch((error2) => {
602
641
  log("Retry refresh failed:", error2);
603
642
  });
604
643
  }
@@ -624,9 +663,6 @@ function BasicProvider({
624
663
  syncRef.current.syncable.on("statusChanged", (status, url) => {
625
664
  setDbStatus(getSyncStatus(status));
626
665
  });
627
- syncRef.current.syncable.getStatus().then((status) => {
628
- setDbStatus(getSyncStatus(status));
629
- });
630
666
  if (options.shouldConnect) {
631
667
  setShouldConnect(true);
632
668
  } else {
@@ -636,17 +672,15 @@ function BasicProvider({
636
672
  }
637
673
  }
638
674
  async function checkSchema() {
639
- const valid = validateSchema3(schema);
640
- if (!valid.valid) {
641
- log("Basic Schema is invalid!", valid.errors);
642
- console.group("Schema Errors");
675
+ const result = await validateAndCheckSchema(schema);
676
+ if (!result.isValid) {
643
677
  let errorMessage = "";
644
- valid.errors.forEach((error2, index) => {
645
- log(`${index + 1}:`, error2.message, ` - at ${error2.instancePath}`);
646
- errorMessage += `${index + 1}: ${error2.message} - at ${error2.instancePath}
678
+ if (result.errors) {
679
+ result.errors.forEach((error2, index) => {
680
+ errorMessage += `${index + 1}: ${error2.message} - at ${error2.instancePath}
647
681
  `;
648
- });
649
- console.groupEnd("Schema Errors");
682
+ });
683
+ }
650
684
  setError({
651
685
  code: "schema_invalid",
652
686
  title: "Basic Schema is invalid!",
@@ -655,17 +689,10 @@ function BasicProvider({
655
689
  setIsReady(true);
656
690
  return null;
657
691
  }
658
- let schemaStatus = { valid: false };
659
- if (schema.version !== 0) {
660
- schemaStatus = await getSchemaStatus(schema);
661
- log("schemaStatus", schemaStatus);
662
- } else {
663
- log("schema not published - at version 0");
664
- }
665
- if (schemaStatus.valid) {
692
+ if (result.schemaStatus.valid) {
666
693
  initDb({ shouldConnect: true });
667
694
  } else {
668
- log("Schema is invalid!", schemaStatus);
695
+ log("Schema is invalid!", result.schemaStatus);
669
696
  initDb({ shouldConnect: false });
670
697
  }
671
698
  checkForNewVersion();
@@ -677,43 +704,61 @@ function BasicProvider({
677
704
  }
678
705
  }, []);
679
706
  useEffect(() => {
680
- if (token && syncRef.current && isSignedIn && shouldConnect) {
681
- connectToDb();
707
+ async function connectToDb() {
708
+ if (token && syncRef.current && isSignedIn && shouldConnect) {
709
+ const tok = await getToken();
710
+ if (!tok) {
711
+ log("no token found");
712
+ return;
713
+ }
714
+ log("connecting to db...");
715
+ syncRef.current?.connect({ access_token: tok }).catch((e) => {
716
+ log("error connecting to db", e);
717
+ });
718
+ }
682
719
  }
720
+ connectToDb();
683
721
  }, [isSignedIn, shouldConnect]);
684
- const connectToDb = async () => {
685
- const tok = await getToken();
686
- if (!tok) {
687
- log("no token found");
688
- return;
689
- }
690
- log("connecting to db...");
691
- syncRef.current.connect({ access_token: tok }).catch((e) => {
692
- log("error connecting to db", e);
693
- });
694
- };
695
722
  useEffect(() => {
696
723
  const initializeAuth = async () => {
697
724
  await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
725
+ try {
726
+ const versionUpdater = createVersionUpdater(storageAdapter, version, getMigrations());
727
+ const updateResult = await versionUpdater.checkAndUpdate();
728
+ if (updateResult.updated) {
729
+ log(`App updated from ${updateResult.fromVersion} to ${updateResult.toVersion}`);
730
+ } else {
731
+ log(`App version ${updateResult.toVersion} is current`);
732
+ }
733
+ } catch (error2) {
734
+ log("Version update failed:", error2);
735
+ }
698
736
  try {
699
737
  if (window.location.search.includes("code")) {
700
- let code = window.location?.search?.split("code=")[1].split("&")[0];
738
+ let code = window.location?.search?.split("code=")[1]?.split("&")[0];
739
+ if (!code)
740
+ return;
701
741
  const state = await storageAdapter.get(STORAGE_KEYS.AUTH_STATE);
702
- if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
742
+ const urlState = window.location.search.split("state=")[1]?.split("&")[0];
743
+ if (!state || state !== urlState) {
703
744
  log("error: auth state does not match");
704
745
  setIsAuthReady(true);
705
746
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
706
- cleanOAuthParamsFromUrl();
747
+ cleanOAuthParams();
707
748
  return;
708
749
  }
709
750
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
710
- cleanOAuthParamsFromUrl();
711
- fetchToken(code);
751
+ cleanOAuthParams();
752
+ fetchToken(code, false).catch((error2) => {
753
+ log("Error fetching token:", error2);
754
+ });
712
755
  } else {
713
756
  const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
714
757
  if (refreshToken) {
715
758
  log("Found refresh token in storage, attempting to refresh access token");
716
- fetchToken(refreshToken);
759
+ fetchToken(refreshToken, true).catch((error2) => {
760
+ log("Error fetching refresh token:", error2);
761
+ });
717
762
  } else {
718
763
  let cookie_token = getCookie("basic_token");
719
764
  if (cookie_token !== "") {
@@ -762,8 +807,8 @@ function BasicProvider({
762
807
  }
763
808
  await storageAdapter.set(STORAGE_KEYS.USER_INFO, JSON.stringify(user2));
764
809
  log("Cached user info in storage");
765
- document.cookie = `basic_access_token=${token.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
766
- document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
810
+ setCookie("basic_access_token", token?.access_token || "", { httpOnly: false });
811
+ setCookie("basic_token", JSON.stringify(token));
767
812
  setUser(user2);
768
813
  setIsSignedIn(true);
769
814
  setIsAuthReady(true);
@@ -780,19 +825,19 @@ function BasicProvider({
780
825
  if (isExpired) {
781
826
  log("token is expired - refreshing ...");
782
827
  try {
783
- const newToken = await fetchToken(token?.refresh_token);
784
- fetchUser(newToken.access_token);
828
+ const newToken = await fetchToken(token?.refresh_token || "", true);
829
+ fetchUser(newToken?.access_token || "");
785
830
  } catch (error2) {
786
831
  log("Failed to refresh token in checkToken:", error2);
787
832
  if (error2.message.includes("offline") || error2.message.includes("Network")) {
788
833
  log("Network issue - continuing with expired token until online");
789
- fetchUser(token.access_token);
834
+ fetchUser(token?.access_token || "");
790
835
  } else {
791
836
  setIsAuthReady(true);
792
837
  }
793
838
  }
794
839
  } else {
795
- fetchUser(token.access_token);
840
+ fetchUser(token?.access_token || "");
796
841
  }
797
842
  }
798
843
  if (token) {
@@ -811,14 +856,14 @@ function BasicProvider({
811
856
  if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
812
857
  throw new Error("Invalid redirect URI provided");
813
858
  }
814
- let baseUrl2 = "https://api.basic.tech/auth/authorize";
815
- baseUrl2 += `?client_id=${project_id}`;
816
- baseUrl2 += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
817
- baseUrl2 += `&response_type=code`;
818
- baseUrl2 += `&scope=profile`;
819
- baseUrl2 += `&state=${randomState}`;
859
+ let baseUrl = "https://api.basic.tech/auth/authorize";
860
+ baseUrl += `?client_id=${project_id}`;
861
+ baseUrl += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
862
+ baseUrl += `&response_type=code`;
863
+ baseUrl += `&scope=profile`;
864
+ baseUrl += `&state=${randomState}`;
820
865
  log("Generated sign-in link successfully");
821
- return baseUrl2;
866
+ return baseUrl;
822
867
  } catch (error2) {
823
868
  log("Error generating sign-in link:", error2);
824
869
  throw error2;
@@ -840,7 +885,7 @@ function BasicProvider({
840
885
  window.location.href = signInLink;
841
886
  } catch (error2) {
842
887
  log("Error during sign-in:", error2);
843
- if (isDevelopment()) {
888
+ if (isDevMode()) {
844
889
  setError({
845
890
  code: "signin_error",
846
891
  title: "Sign-in Failed",
@@ -864,8 +909,8 @@ function BasicProvider({
864
909
  }
865
910
  }
866
911
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
867
- cleanOAuthParamsFromUrl();
868
- const token2 = await fetchToken(code);
912
+ cleanOAuthParams();
913
+ const token2 = await fetchToken(code, false);
869
914
  if (token2) {
870
915
  log("signinWithCode successful");
871
916
  return { success: true };
@@ -885,16 +930,16 @@ function BasicProvider({
885
930
  setUser({});
886
931
  setIsSignedIn(false);
887
932
  setToken(null);
888
- document.cookie = `basic_token=; Secure; SameSite=Strict`;
889
- document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
933
+ clearCookie("basic_token");
934
+ clearCookie("basic_access_token");
890
935
  await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
891
936
  await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
892
937
  await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
893
938
  if (syncRef.current) {
894
939
  (async () => {
895
940
  try {
896
- await syncRef.current.close();
897
- await syncRef.current.delete({ disableAutoOpen: false });
941
+ await syncRef.current?.close();
942
+ await syncRef.current?.delete({ disableAutoOpen: false });
898
943
  syncRef.current = null;
899
944
  window?.location?.reload();
900
945
  } catch (error2) {
@@ -910,7 +955,7 @@ function BasicProvider({
910
955
  if (refreshToken) {
911
956
  log("No token in memory, attempting to refresh from storage");
912
957
  try {
913
- const newToken = await fetchToken(refreshToken);
958
+ const newToken = await fetchToken(refreshToken, true);
914
959
  if (newToken?.access_token) {
915
960
  return newToken.access_token;
916
961
  }
@@ -937,7 +982,7 @@ function BasicProvider({
937
982
  const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
938
983
  if (refreshToken) {
939
984
  try {
940
- const newToken = await fetchToken(refreshToken);
985
+ const newToken = await fetchToken(refreshToken, true);
941
986
  return newToken?.access_token || "";
942
987
  } catch (error2) {
943
988
  log("Failed to refresh expired token:", error2);
@@ -953,33 +998,26 @@ function BasicProvider({
953
998
  }
954
999
  return token?.access_token || "";
955
1000
  };
956
- function getCookie(name) {
957
- let cookieValue = "";
958
- if (document.cookie && document.cookie !== "") {
959
- const cookies = document.cookie.split(";");
960
- for (let i = 0; i < cookies.length; i++) {
961
- const cookie = cookies[i].trim();
962
- if (cookie.substring(0, name.length + 1) === name + "=") {
963
- cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
964
- break;
965
- }
966
- }
967
- }
968
- return cookieValue;
969
- }
970
- const fetchToken = async (code) => {
1001
+ const fetchToken = async (codeOrRefreshToken, isRefreshToken = false) => {
971
1002
  try {
972
1003
  if (!isOnline) {
973
1004
  log("Network is offline, marking refresh as pending");
974
1005
  setPendingRefresh(true);
975
1006
  throw new Error("Network offline - refresh will be retried when online");
976
1007
  }
1008
+ const requestBody = isRefreshToken ? {
1009
+ grant_type: "refresh_token",
1010
+ refresh_token: codeOrRefreshToken
1011
+ } : {
1012
+ grant_type: "authorization_code",
1013
+ code: codeOrRefreshToken
1014
+ };
977
1015
  const token2 = await fetch("https://api.basic.tech/auth/token", {
978
1016
  method: "POST",
979
1017
  headers: {
980
1018
  "Content-Type": "application/json"
981
1019
  },
982
- body: JSON.stringify({ code })
1020
+ body: JSON.stringify(requestBody)
983
1021
  }).then((response) => response.json()).catch((error2) => {
984
1022
  log("Network error fetching token:", error2);
985
1023
  if (!isOnline) {
@@ -996,8 +1034,8 @@ function BasicProvider({
996
1034
  }
997
1035
  await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
998
1036
  await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
999
- document.cookie = `basic_token=; Secure; SameSite=Strict`;
1000
- document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1037
+ clearCookie("basic_token");
1038
+ clearCookie("basic_access_token");
1001
1039
  setUser({});
1002
1040
  setIsSignedIn(false);
1003
1041
  setToken(null);
@@ -1010,7 +1048,7 @@ function BasicProvider({
1010
1048
  await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token2.refresh_token);
1011
1049
  log("Updated refresh token in storage");
1012
1050
  }
1013
- document.cookie = `basic_access_token=${token2.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
1051
+ setCookie("basic_access_token", token2.access_token, { httpOnly: false });
1014
1052
  log("Updated access token in cookie");
1015
1053
  }
1016
1054
  return token2;
@@ -1019,8 +1057,8 @@ function BasicProvider({
1019
1057
  if (!error2.message.includes("offline") && !error2.message.includes("Network")) {
1020
1058
  await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
1021
1059
  await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
1022
- document.cookie = `basic_token=; Secure; SameSite=Strict`;
1023
- document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1060
+ clearCookie("basic_token");
1061
+ clearCookie("basic_access_token");
1024
1062
  setUser({});
1025
1063
  setIsSignedIn(false);
1026
1064
  setToken(null);
@@ -1029,35 +1067,6 @@ function BasicProvider({
1029
1067
  throw error2;
1030
1068
  }
1031
1069
  };
1032
- const db_ = (tableName) => {
1033
- const checkSignIn = () => {
1034
- if (!isSignedIn) {
1035
- throw new Error("cannot use db. user not logged in.");
1036
- }
1037
- };
1038
- return {
1039
- get: async () => {
1040
- checkSignIn();
1041
- const tok = await getToken();
1042
- return get({ projectId: project_id, accountId: user.id, tableName, token: tok });
1043
- },
1044
- add: async (value) => {
1045
- checkSignIn();
1046
- const tok = await getToken();
1047
- return add({ projectId: project_id, accountId: user.id, tableName, value, token: tok });
1048
- },
1049
- update: async (id, value) => {
1050
- checkSignIn();
1051
- const tok = await getToken();
1052
- return update({ projectId: project_id, accountId: user.id, tableName, id, value, token: tok });
1053
- },
1054
- delete: async (id) => {
1055
- checkSignIn();
1056
- const tok = await getToken();
1057
- return deleteRecord({ projectId: project_id, accountId: user.id, tableName, id, token: tok });
1058
- }
1059
- };
1060
- };
1061
1070
  const noDb = {
1062
1071
  collection: () => {
1063
1072
  throw new Error("no basicdb found - initialization failed. double check your schema.");
@@ -1076,7 +1085,7 @@ function BasicProvider({
1076
1085
  db: syncRef.current ? syncRef.current : noDb,
1077
1086
  dbStatus
1078
1087
  }, children: [
1079
- error && isDevelopment() && /* @__PURE__ */ jsx(ErrorDisplay, { error }),
1088
+ error && isDevMode() && /* @__PURE__ */ jsx(ErrorDisplay, { error }),
1080
1089
  isReady && children
1081
1090
  ] });
1082
1091
  }