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